2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = (
9 Roo.each(document.styleSheets, function(s) {
10 if (s.href.match(/css-bootstrap4/)) {
18 * base class for bootstrap elements.
22 Roo.bootstrap = Roo.bootstrap || {};
24 * @class Roo.bootstrap.Component
25 * @extends Roo.Component
26 * Bootstrap Component base class
27 * @cfg {String} cls css class
28 * @cfg {String} style any extra css
29 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
31 * @cfg {string} dataId cutomer id
32 * @cfg {string} name Specifies name attribute
33 * @cfg {string} tooltip Text for the tooltip
34 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
35 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
38 * Do not use directly - it does not do anything..
39 * @param {Object} config The config object
44 Roo.bootstrap.Component = function(config){
45 Roo.bootstrap.Component.superclass.constructor.call(this, config);
49 * @event childrenrendered
50 * Fires when the children have been rendered..
51 * @param {Roo.bootstrap.Component} this
53 "childrenrendered" : true
61 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
64 allowDomMove : false, // to stop relocations in parent onRender...
74 * Initialize Events for the element
76 initEvents : function() { },
82 can_build_overlaid : true,
84 container_method : false,
91 // returns the parent component..
92 return Roo.ComponentMgr.get(this.parentId)
98 onRender : function(ct, position)
100 // Roo.log("Call onRender: " + this.xtype);
102 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
105 if (this.el.attr('xtype')) {
106 this.el.attr('xtypex', this.el.attr('xtype'));
107 this.el.dom.removeAttribute('xtype');
117 var cfg = Roo.apply({}, this.getAutoCreate());
119 cfg.id = this.id || Roo.id();
121 // fill in the extra attributes
122 if (this.xattr && typeof(this.xattr) =='object') {
123 for (var i in this.xattr) {
124 cfg[i] = this.xattr[i];
129 cfg.dataId = this.dataId;
133 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
136 if (this.style) { // fixme needs to support more complex style data.
137 cfg.style = this.style;
141 cfg.name = this.name;
144 this.el = ct.createChild(cfg, position);
147 this.tooltipEl().attr('tooltip', this.tooltip);
150 if(this.tabIndex !== undefined){
151 this.el.dom.setAttribute('tabIndex', this.tabIndex);
158 * Fetch the element to add children to
159 * @return {Roo.Element} defaults to this.el
161 getChildContainer : function()
166 * Fetch the element to display the tooltip on.
167 * @return {Roo.Element} defaults to this.el
169 tooltipEl : function()
174 * This is really a wrapper for addxtypeChild
175 * it handles stuff relating to flexy:foreach / flexy:if
176 * = some of our projects use a flat rendering of the output, and try and overlay it with dynamic data.
177 * -- this is a bit of a nightmare... and is even more confusing to debug..
182 addxtype : function(tree,cntr)
184 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186 var cn = Roo.factory(tree); // this is posibly the first of two times that the ctor get's called...
187 cn.parentType = this.xtype; //??
188 cn.parentId = this.id;
189 if (typeof(cn.container_method) == 'string') {
190 cntr = cn.container_method;
194 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
196 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
198 var build_from_html = Roo.XComponent.build_from_html;
200 var is_body = (tree.xtype == 'Body') ;
202 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
204 var self_cntr_el = Roo.get(this[cntr](false));
206 // do not try and build conditional elements
207 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
211 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
212 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
213 //return this.addxtypeChild(tree,cntr, is_body);
214 return this.addxtypeChild(tree, cntr, is_body);
217 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
220 return this.addxtypeChild(Roo.apply({}, tree),cntr);
223 Roo.log('skipping render');
229 if (!build_from_html) {
233 // this i think handles overlaying multiple children of the same type
234 // with the sam eelement.. - which might be buggy..
236 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
242 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
246 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
252 * add a child to this element
253 * - turn the child.cfg into a child_instance
254 * - call child_instance.render( this { getContainerMethod()} )
255 * - loop through the children, and call addxtype.. (reall this) on newly created child.
259 addxtypeChild : function (tree, cntr, is_body)
261 Roo.debug && Roo.log('addxtypeChild:' + cntr);
263 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
266 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
267 (typeof(tree['flexy:foreach']) != 'undefined');
271 skip_children = false;
272 // render the element if it's not BODY.
275 // if parent was disabled, then do not try and create the children..
276 if(!this[cntr](true)){
281 cn = Roo.factory(tree);
283 cn.parentType = this.xtype; //??
284 cn.parentId = this.id;
286 var build_from_html = Roo.XComponent.build_from_html;
289 // does the container contain child eleemnts with 'xtype' attributes.
290 // that match this xtype..
291 // note - when we render we create these as well..
292 // so we should check to see if body has xtype set.
293 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
295 var self_cntr_el = Roo.get(this[cntr](false));
296 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
298 //Roo.log(Roo.XComponent.build_from_html);
299 //Roo.log("got echild:");
302 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
303 // and are not displayed -this causes this to use up the wrong element when matching.
304 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
307 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
308 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
314 //echild.dom.removeAttribute('xtype');
316 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
317 Roo.debug && Roo.log(self_cntr_el);
318 Roo.debug && Roo.log(echild);
319 Roo.debug && Roo.log(cn);
325 // if object has flexy:if - then it may or may not be rendered.
326 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
327 // skip a flexy if element.
328 Roo.debug && Roo.log('skipping render');
329 Roo.debug && Roo.log(tree);
331 Roo.debug && Roo.log('skipping all children');
332 skip_children = true;
337 // actually if flexy:foreach is found, we really want to create
338 // multiple copies here...
340 //Roo.log(this[cntr]());
341 // some elements do not have render methods.. like the layouts...
343 if(this[cntr](true) === false){
348 cn.render && cn.render(this[cntr](true));
351 // then add the element..
358 cn.addxtypeChildren(tree.items, skip_children);
364 * add a number of children to this object,
365 * which in turn calls render...
369 addxtypeChildren: function(child_array, skip_children)
372 if (!child_array || !child_array.length ) {
377 for(var i =0;i < child_array.length;i++) {
381 // Roo.log(['add child', items[i]]);
382 nitems.push(this.addxtype(Roo.apply({}, child_array[i])));
386 this.fireEvent('childrenrendered', this);
396 * xAddChildren - the 'sub-compentized' version of the above idea..
398 xAddChildren: function(child_array, skip_children)
401 if (!child_array || !child_array.length ) {
406 for(var i =0;i < child_array.length;i++) {
410 // Roo.log(['add child', items[i]]);
411 nitems.push(this.xAdd(Roo.apply({}, child_array[i])));
415 this.fireEvent('childrenrendered', this);
421 * add a child to this element
422 * - turn the child.cfg into a child_instance
423 * - call child_instance.render( this { getContainerMethod()} )
424 * - loop through the children, and call addxtype.. (reall this) on newly created child.
428 xAdd : function (tree, cntr, is_body)
430 Roo.debug && Roo.log('xadd:' + cntr);
432 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
434 var parent_ctnr = this[cntr](true);
435 if(parent_ctnr === false){
436 return false; // getChildContainer an return false explicitly to block children being added?
439 throw new Exception("could not find parent Container for item");
442 var cn = Roo.factory(tree);
443 // at this point items[] array may be set..
444 // constructors should not really be building their children?
445 cn.parentType = this.xtype; //??
446 cn.parentId = this.id;
449 cn.render && cn.render(parent_ctnr);
451 cn.xAddChildren(tree.items || cn.items);
452 delete tree.items; // not really needed?
458 * Set the element that will be used to show or hide
460 setVisibilityEl : function(el)
462 this.visibilityEl = el;
466 * Get the element that will be used to show or hide
468 getVisibilityEl : function()
470 if (typeof(this.visibilityEl) == 'object') {
471 return this.visibilityEl;
474 if (typeof(this.visibilityEl) == 'string') {
475 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
482 * Show a component - removes 'hidden' class
486 if(!this.getVisibilityEl()){
490 this.getVisibilityEl().removeClass(['hidden','d-none']);
492 this.fireEvent('show', this);
497 * Hide a component - adds 'hidden' class
501 if(!this.getVisibilityEl()){
505 this.getVisibilityEl().addClass(['hidden','d-none']);
507 this.fireEvent('hide', this);
520 * @class Roo.bootstrap.Body
521 * @extends Roo.bootstrap.Component
522 * Bootstrap Body class
526 * @param {Object} config The config object
527 * @cfg {DomElement} do_render - if this is set, then the constructor will try and initialize render, using this as the start point
531 Roo.bootstrap.Body = function(config){
533 config = config || {};
535 Roo.bootstrap.Body.superclass.constructor.call(this, config);
536 this.el = Roo.get(config.el ? config.el : document.body );
537 if (this.cls && this.cls.length) {
538 Roo.get(document.body).addClass(this.cls);
540 if (config.do_render) {
541 this.onRender(config.do_render, '');
542 this.xAddChildren(config.items);
547 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
550 is_body : true,// just to make sure it's constructed?
555 onRender : function(ct, position)
557 if (!this.do_render) {
560 this.el = Roo.get(this.do_render);
561 /* Roo.log("Roo.bootstrap.Body - onRender");
562 if (this.cls && this.cls.length) {
563 Roo.get(document.body).addClass(this.cls);
582 * @class Roo.bootstrap.ButtonGroup
583 * @extends Roo.bootstrap.Component
584 * Bootstrap ButtonGroup class
585 * @cfg {String} size lg | sm | xs (default empty normal)
586 * @cfg {String} align vertical | justified (default none)
587 * @cfg {String} direction up | down (default down)
588 * @cfg {Boolean} toolbar false | true
589 * @cfg {Boolean} btn true | false
594 * @param {Object} config The config object
597 Roo.bootstrap.ButtonGroup = function(config){
598 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
601 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
609 getAutoCreate : function(){
615 cfg.html = this.html || cfg.html;
626 if (['vertical','justified'].indexOf(this.align)!==-1) {
627 cfg.cls = 'btn-group-' + this.align;
629 if (this.align == 'justified') {
630 console.log(this.items);
634 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
635 cfg.cls += ' btn-group-' + this.size;
638 if (this.direction == 'up') {
639 cfg.cls += ' dropup' ;
645 * Add a button to the group (similar to NavItem API.)
647 addItem : function(cfg)
649 var cn = new Roo.bootstrap.Button(cfg);
651 cn.parentId = this.id;
652 cn.onRender(this.el, null);
666 * @class Roo.bootstrap.Button
667 * @extends Roo.bootstrap.Component
668 * Bootstrap Button class
669 * @cfg {String} html The button content
670 * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
671 * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
672 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
673 * @cfg {String} size ( lg | sm | xs)
674 * @cfg {String} tag ( a | input | submit)
675 * @cfg {String} href empty or href
676 * @cfg {Boolean} disabled default false;
677 * @cfg {Boolean} isClose default false;
678 * @cfg {String} glyphicon depricated - use fa
679 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
680 * @cfg {String} badge text for badge
681 * @cfg {String} theme (default|glow)
682 * @cfg {Boolean} inverse dark themed version
683 * @cfg {Boolean} toggle is it a slidy toggle button
684 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
685 * @cfg {String} ontext text for on slidy toggle state
686 * @cfg {String} offtext text for off slidy toggle state
687 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
688 * @cfg {Boolean} removeClass remove the standard class..
689 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
692 * Create a new button
693 * @param {Object} config The config object
697 Roo.bootstrap.Button = function(config){
698 Roo.bootstrap.Button.superclass.constructor.call(this, config);
699 this.weightClass = ["btn-default btn-outline-secondary",
711 * When a butotn is pressed
712 * @param {Roo.bootstrap.Button} btn
713 * @param {Roo.EventObject} e
718 * After the button has been toggles
719 * @param {Roo.bootstrap.Button} btn
720 * @param {Roo.EventObject} e
721 * @param {boolean} pressed (also available as button.pressed)
727 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
748 preventDefault: true,
756 getAutoCreate : function(){
764 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
765 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
770 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
772 if (this.toggle == true) {
775 cls: 'slider-frame roo-button',
780 'data-off-text':'OFF',
781 cls: 'slider-button',
787 if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
788 cfg.cls += ' '+this.weight;
797 cfg["aria-hidden"] = true;
799 cfg.html = "×";
805 if (this.theme==='default') {
806 cfg.cls = 'btn roo-button';
808 //if (this.parentType != 'Navbar') {
809 this.weight = this.weight.length ? this.weight : 'default';
811 if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
813 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
814 var weight = this.weight == 'default' ? 'secondary' : this.weight;
815 cfg.cls += ' btn-' + outline + weight;
816 if (this.weight == 'default') {
818 cfg.cls += ' btn-' + this.weight;
821 } else if (this.theme==='glow') {
824 cfg.cls = 'btn-glow roo-button';
826 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
828 cfg.cls += ' ' + this.weight;
834 this.cls += ' inverse';
838 if (this.active || this.pressed === true) {
839 cfg.cls += ' active';
843 cfg.disabled = 'disabled';
847 Roo.log('changing to ul' );
849 this.glyphicon = 'caret';
850 if (Roo.bootstrap.version == 4) {
851 this.fa = 'caret-down';
856 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
858 //gsRoo.log(this.parentType);
859 if (this.parentType === 'Navbar' && !this.parent().bar) {
860 Roo.log('changing to li?');
869 href : this.href || '#'
872 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
873 cfg.cls += ' dropdown';
880 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
882 if (this.glyphicon) {
883 cfg.html = ' ' + cfg.html;
888 cls: 'glyphicon glyphicon-' + this.glyphicon
893 cfg.html = ' ' + cfg.html;
898 cls: 'fa fas fa-' + this.fa
908 // cfg.cls='btn roo-button';
912 var value = cfg.html;
917 cls: 'glyphicon glyphicon-' + this.glyphicon,
924 cls: 'fa fas fa-' + this.fa,
929 var bw = this.badge_weight.length ? this.badge_weight :
930 (this.weight.length ? this.weight : 'secondary');
931 bw = bw == 'default' ? 'secondary' : bw;
937 cls: 'badge badge-' + bw,
946 cfg.cls += ' dropdown';
947 cfg.html = typeof(cfg.html) != 'undefined' ?
948 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
951 if (cfg.tag !== 'a' && this.href !== '') {
952 throw "Tag must be a to set href.";
953 } else if (this.href.length > 0) {
954 cfg.href = this.href;
957 if(this.removeClass){
962 cfg.target = this.target;
967 initEvents: function() {
968 // Roo.log('init events?');
969 // Roo.log(this.el.dom);
972 if (typeof (this.menu) != 'undefined') {
973 this.menu.parentType = this.xtype;
974 this.menu.triggerEl = this.el;
975 this.addxtype(Roo.apply({}, this.menu));
979 if (this.el.hasClass('roo-button')) {
980 this.el.on('click', this.onClick, this);
982 this.el.select('.roo-button').on('click', this.onClick, this);
985 if(this.removeClass){
986 this.el.on('click', this.onClick, this);
989 this.el.enableDisplayMode();
992 onClick : function(e)
998 Roo.log('button on click ');
999 if(this.preventDefault){
1003 if (this.pressed === true || this.pressed === false) {
1004 this.toggleActive(e);
1008 this.fireEvent('click', this, e);
1012 * Enables this button
1016 this.disabled = false;
1017 this.el.removeClass('disabled');
1021 * Disable this button
1023 disable : function()
1025 this.disabled = true;
1026 this.el.addClass('disabled');
1029 * sets the active state on/off,
1030 * @param {Boolean} state (optional) Force a particular state
1032 setActive : function(v) {
1034 this.el[v ? 'addClass' : 'removeClass']('active');
1038 * toggles the current active state
1040 toggleActive : function(e)
1042 this.setActive(!this.pressed);
1043 this.fireEvent('toggle', this, e, !this.pressed);
1046 * get the current active state
1047 * @return {boolean} true if it's active
1049 isActive : function()
1051 return this.el.hasClass('active');
1054 * set the text of the first selected button
1056 setText : function(str)
1058 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1061 * get the text of the first selected button
1063 getText : function()
1065 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1068 setWeight : function(str)
1070 this.el.removeClass(this.weightClass);
1072 var outline = this.outline ? 'outline-' : '';
1073 if (str == 'default') {
1074 this.el.addClass('btn-default btn-outline-secondary');
1077 this.el.addClass('btn-' + outline + str);
1091 * @class Roo.bootstrap.Column
1092 * @extends Roo.bootstrap.Component
1093 * Bootstrap Column class
1094 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1095 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1096 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1097 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1098 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1099 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1100 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1101 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1104 * @cfg {Boolean} hidden (true|false) hide the element
1105 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1106 * @cfg {String} fa (ban|check|...) font awesome icon
1107 * @cfg {Number} fasize (1|2|....) font awsome size
1109 * @cfg {String} icon (info-sign|check|...) glyphicon name
1111 * @cfg {String} html content of column.
1114 * Create a new Column
1115 * @param {Object} config The config object
1118 Roo.bootstrap.Column = function(config){
1119 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1122 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1140 getAutoCreate : function(){
1141 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1149 ['xs','sm','md','lg'].map(function(size){
1150 //Roo.log( size + ':' + settings[size]);
1152 if (settings[size+'off'] !== false) {
1153 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1156 if (settings[size] === false) {
1160 if (!settings[size]) { // 0 = hidden
1161 cfg.cls += ' hidden-' + size + ' hidden' + size + '-down';;
1164 cfg.cls += ' col-' + size + '-' + settings[size] + (
1165 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1171 cfg.cls += ' hidden';
1174 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1175 cfg.cls +=' alert alert-' + this.alert;
1179 if (this.html.length) {
1180 cfg.html = this.html;
1184 if (this.fasize > 1) {
1185 fasize = ' fa-' + this.fasize + 'x';
1187 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1192 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1211 * @class Roo.bootstrap.Container
1212 * @extends Roo.bootstrap.Component
1213 * Bootstrap Container class
1214 * @cfg {Boolean} jumbotron is it a jumbotron element
1215 * @cfg {String} html content of element
1216 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1217 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1218 * @cfg {String} header content of header (for panel)
1219 * @cfg {String} footer content of footer (for panel)
1220 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1221 * @cfg {String} tag (header|aside|section) type of HTML tag.
1222 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1223 * @cfg {String} fa font awesome icon
1224 * @cfg {String} icon (info-sign|check|...) glyphicon name
1225 * @cfg {Boolean} hidden (true|false) hide the element
1226 * @cfg {Boolean} expandable (true|false) default false
1227 * @cfg {Boolean} expanded (true|false) default true
1228 * @cfg {String} rheader contet on the right of header
1229 * @cfg {Boolean} clickable (true|false) default false
1233 * Create a new Container
1234 * @param {Object} config The config object
1237 Roo.bootstrap.Container = function(config){
1238 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1244 * After the panel has been expand
1246 * @param {Roo.bootstrap.Container} this
1251 * After the panel has been collapsed
1253 * @param {Roo.bootstrap.Container} this
1258 * When a element is chick
1259 * @param {Roo.bootstrap.Container} this
1260 * @param {Roo.EventObject} e
1266 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1284 getChildContainer : function() {
1290 if (this.panel.length) {
1291 return this.el.select('.panel-body',true).first();
1298 getAutoCreate : function(){
1301 tag : this.tag || 'div',
1305 if (this.jumbotron) {
1306 cfg.cls = 'jumbotron';
1311 // - this is applied by the parent..
1313 // cfg.cls = this.cls + '';
1316 if (this.sticky.length) {
1318 var bd = Roo.get(document.body);
1319 if (!bd.hasClass('bootstrap-sticky')) {
1320 bd.addClass('bootstrap-sticky');
1321 Roo.select('html',true).setStyle('height', '100%');
1324 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1328 if (this.well.length) {
1329 switch (this.well) {
1332 cfg.cls +=' well well-' +this.well;
1341 cfg.cls += ' hidden';
1345 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1346 cfg.cls +=' alert alert-' + this.alert;
1351 if (this.panel.length) {
1352 cfg.cls += ' panel panel-' + this.panel;
1354 if (this.header.length) {
1358 if(this.expandable){
1360 cfg.cls = cfg.cls + ' expandable';
1364 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1372 cls : 'panel-title',
1373 html : (this.expandable ? ' ' : '') + this.header
1377 cls: 'panel-header-right',
1383 cls : 'panel-heading',
1384 style : this.expandable ? 'cursor: pointer' : '',
1392 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1397 if (this.footer.length) {
1399 cls : 'panel-footer',
1408 body.html = this.html || cfg.html;
1409 // prefix with the icons..
1411 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1414 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1419 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1420 cfg.cls = 'container';
1426 initEvents: function()
1428 if(this.expandable){
1429 var headerEl = this.headerEl();
1432 headerEl.on('click', this.onToggleClick, this);
1437 this.el.on('click', this.onClick, this);
1442 onToggleClick : function()
1444 var headerEl = this.headerEl();
1460 if(this.fireEvent('expand', this)) {
1462 this.expanded = true;
1464 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1466 this.el.select('.panel-body',true).first().removeClass('hide');
1468 var toggleEl = this.toggleEl();
1474 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1479 collapse : function()
1481 if(this.fireEvent('collapse', this)) {
1483 this.expanded = false;
1485 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1486 this.el.select('.panel-body',true).first().addClass('hide');
1488 var toggleEl = this.toggleEl();
1494 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1498 toggleEl : function()
1500 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1504 return this.el.select('.panel-heading .fa',true).first();
1507 headerEl : function()
1509 if(!this.el || !this.panel.length || !this.header.length){
1513 return this.el.select('.panel-heading',true).first()
1518 if(!this.el || !this.panel.length){
1522 return this.el.select('.panel-body',true).first()
1525 titleEl : function()
1527 if(!this.el || !this.panel.length || !this.header.length){
1531 return this.el.select('.panel-title',true).first();
1534 setTitle : function(v)
1536 var titleEl = this.titleEl();
1542 titleEl.dom.innerHTML = v;
1545 getTitle : function()
1548 var titleEl = this.titleEl();
1554 return titleEl.dom.innerHTML;
1557 setRightTitle : function(v)
1559 var t = this.el.select('.panel-header-right',true).first();
1565 t.dom.innerHTML = v;
1568 onClick : function(e)
1572 this.fireEvent('click', this, e);
1585 * @class Roo.bootstrap.Img
1586 * @extends Roo.bootstrap.Component
1587 * Bootstrap Img class
1588 * @cfg {Boolean} imgResponsive false | true
1589 * @cfg {String} border rounded | circle | thumbnail
1590 * @cfg {String} src image source
1591 * @cfg {String} alt image alternative text
1592 * @cfg {String} href a tag href
1593 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1594 * @cfg {String} xsUrl xs image source
1595 * @cfg {String} smUrl sm image source
1596 * @cfg {String} mdUrl md image source
1597 * @cfg {String} lgUrl lg image source
1600 * Create a new Input
1601 * @param {Object} config The config object
1604 Roo.bootstrap.Img = function(config){
1605 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1611 * The img click event for the img.
1612 * @param {Roo.EventObject} e
1618 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1620 imgResponsive: true,
1630 getAutoCreate : function()
1632 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1633 return this.createSingleImg();
1638 cls: 'roo-image-responsive-group',
1643 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1645 if(!_this[size + 'Url']){
1651 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1652 html: _this.html || cfg.html,
1653 src: _this[size + 'Url']
1656 img.cls += ' roo-image-responsive-' + size;
1658 var s = ['xs', 'sm', 'md', 'lg'];
1660 s.splice(s.indexOf(size), 1);
1662 Roo.each(s, function(ss){
1663 img.cls += ' hidden-' + ss;
1666 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1667 cfg.cls += ' img-' + _this.border;
1671 cfg.alt = _this.alt;
1684 a.target = _this.target;
1688 cfg.cn.push((_this.href) ? a : img);
1695 createSingleImg : function()
1699 cls: (this.imgResponsive) ? 'img-responsive' : '',
1701 src : 'about:blank' // just incase src get's set to undefined?!?
1704 cfg.html = this.html || cfg.html;
1706 cfg.src = this.src || cfg.src;
1708 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1709 cfg.cls += ' img-' + this.border;
1726 a.target = this.target;
1731 return (this.href) ? a : cfg;
1734 initEvents: function()
1737 this.el.on('click', this.onClick, this);
1742 onClick : function(e)
1744 Roo.log('img onclick');
1745 this.fireEvent('click', this, e);
1748 * Sets the url of the image - used to update it
1749 * @param {String} url the url of the image
1752 setSrc : function(url)
1756 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1757 this.el.dom.src = url;
1761 this.el.select('img', true).first().dom.src = url;
1777 * @class Roo.bootstrap.Link
1778 * @extends Roo.bootstrap.Component
1779 * Bootstrap Link Class
1780 * @cfg {String} alt image alternative text
1781 * @cfg {String} href a tag href
1782 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1783 * @cfg {String} html the content of the link.
1784 * @cfg {String} anchor name for the anchor link
1785 * @cfg {String} fa - favicon
1787 * @cfg {Boolean} preventDefault (true | false) default false
1791 * Create a new Input
1792 * @param {Object} config The config object
1795 Roo.bootstrap.Link = function(config){
1796 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1802 * The img click event for the img.
1803 * @param {Roo.EventObject} e
1809 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1813 preventDefault: false,
1819 getAutoCreate : function()
1821 var html = this.html || '';
1823 if (this.fa !== false) {
1824 html = '<i class="fa fa-' + this.fa + '"></i>';
1829 // anchor's do not require html/href...
1830 if (this.anchor === false) {
1832 cfg.href = this.href || '#';
1834 cfg.name = this.anchor;
1835 if (this.html !== false || this.fa !== false) {
1838 if (this.href !== false) {
1839 cfg.href = this.href;
1843 if(this.alt !== false){
1848 if(this.target !== false) {
1849 cfg.target = this.target;
1855 initEvents: function() {
1857 if(!this.href || this.preventDefault){
1858 this.el.on('click', this.onClick, this);
1862 onClick : function(e)
1864 if(this.preventDefault){
1867 //Roo.log('img onclick');
1868 this.fireEvent('click', this, e);
1881 * @class Roo.bootstrap.Header
1882 * @extends Roo.bootstrap.Component
1883 * Bootstrap Header class
1884 * @cfg {String} html content of header
1885 * @cfg {Number} level (1|2|3|4|5|6) default 1
1888 * Create a new Header
1889 * @param {Object} config The config object
1893 Roo.bootstrap.Header = function(config){
1894 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1897 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1905 getAutoCreate : function(){
1910 tag: 'h' + (1 *this.level),
1911 html: this.html || ''
1923 * Ext JS Library 1.1.1
1924 * Copyright(c) 2006-2007, Ext JS, LLC.
1926 * Originally Released Under LGPL - original licence link has changed is not relivant.
1929 * <script type="text/javascript">
1933 * @class Roo.bootstrap.MenuMgr
1934 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1937 Roo.bootstrap.MenuMgr = function(){
1938 var menus, active, groups = {}, attached = false, lastShow = new Date();
1940 // private - called when first menu is created
1943 active = new Roo.util.MixedCollection();
1944 Roo.get(document).addKeyListener(27, function(){
1945 if(active.length > 0){
1953 if(active && active.length > 0){
1954 var c = active.clone();
1964 if(active.length < 1){
1965 Roo.get(document).un("mouseup", onMouseDown);
1973 var last = active.last();
1974 lastShow = new Date();
1977 Roo.get(document).on("mouseup", onMouseDown);
1982 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1983 m.parentMenu.activeChild = m;
1984 }else if(last && last.isVisible()){
1985 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1990 function onBeforeHide(m){
1992 m.activeChild.hide();
1994 if(m.autoHideTimer){
1995 clearTimeout(m.autoHideTimer);
1996 delete m.autoHideTimer;
2001 function onBeforeShow(m){
2002 var pm = m.parentMenu;
2003 if(!pm && !m.allowOtherMenus){
2005 }else if(pm && pm.activeChild && active != m){
2006 pm.activeChild.hide();
2010 // private this should really trigger on mouseup..
2011 function onMouseDown(e){
2012 Roo.log("on Mouse Up");
2014 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
2015 Roo.log("MenuManager hideAll");
2024 function onBeforeCheck(mi, state){
2026 var g = groups[mi.group];
2027 for(var i = 0, l = g.length; i < l; i++){
2029 g[i].setChecked(false);
2038 * Hides all menus that are currently visible
2040 hideAll : function(){
2045 register : function(menu){
2049 menus[menu.id] = menu;
2050 menu.on("beforehide", onBeforeHide);
2051 menu.on("hide", onHide);
2052 menu.on("beforeshow", onBeforeShow);
2053 menu.on("show", onShow);
2055 if(g && menu.events["checkchange"]){
2059 groups[g].push(menu);
2060 menu.on("checkchange", onCheck);
2065 * Returns a {@link Roo.menu.Menu} object
2066 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
2067 * be used to generate and return a new Menu instance.
2069 get : function(menu){
2070 if(typeof menu == "string"){ // menu id
2072 }else if(menu.events){ // menu instance
2075 /*else if(typeof menu.length == 'number'){ // array of menu items?
2076 return new Roo.bootstrap.Menu({items:menu});
2077 }else{ // otherwise, must be a config
2078 return new Roo.bootstrap.Menu(menu);
2085 unregister : function(menu){
2086 delete menus[menu.id];
2087 menu.un("beforehide", onBeforeHide);
2088 menu.un("hide", onHide);
2089 menu.un("beforeshow", onBeforeShow);
2090 menu.un("show", onShow);
2092 if(g && menu.events["checkchange"]){
2093 groups[g].remove(menu);
2094 menu.un("checkchange", onCheck);
2099 registerCheckable : function(menuItem){
2100 var g = menuItem.group;
2105 groups[g].push(menuItem);
2106 menuItem.on("beforecheckchange", onBeforeCheck);
2111 unregisterCheckable : function(menuItem){
2112 var g = menuItem.group;
2114 groups[g].remove(menuItem);
2115 menuItem.un("beforecheckchange", onBeforeCheck);
2127 * @class Roo.bootstrap.Menu
2128 * @extends Roo.bootstrap.Component
2129 * Bootstrap Menu class - container for MenuItems
2130 * @cfg {String} type (dropdown|treeview|submenu) type of menu
2131 * @cfg {bool} hidden if the menu should be hidden when rendered.
2132 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
2133 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
2137 * @param {Object} config The config object
2141 Roo.bootstrap.Menu = function(config){
2142 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2143 if (this.registerMenu && this.type != 'treeview') {
2144 Roo.bootstrap.MenuMgr.register(this);
2151 * Fires before this menu is displayed (return false to block)
2152 * @param {Roo.menu.Menu} this
2157 * Fires before this menu is hidden (return false to block)
2158 * @param {Roo.menu.Menu} this
2163 * Fires after this menu is displayed
2164 * @param {Roo.menu.Menu} this
2169 * Fires after this menu is hidden
2170 * @param {Roo.menu.Menu} this
2175 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2176 * @param {Roo.menu.Menu} this
2177 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2178 * @param {Roo.EventObject} e
2183 * Fires when the mouse is hovering over this menu
2184 * @param {Roo.menu.Menu} this
2185 * @param {Roo.EventObject} e
2186 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2191 * Fires when the mouse exits this menu
2192 * @param {Roo.menu.Menu} this
2193 * @param {Roo.EventObject} e
2194 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2199 * Fires when a menu item contained in this menu is clicked
2200 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2201 * @param {Roo.EventObject} e
2205 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2208 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2212 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2215 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2217 registerMenu : true,
2219 menuItems :false, // stores the menu items..
2229 getChildContainer : function() {
2233 getAutoCreate : function(){
2235 //if (['right'].indexOf(this.align)!==-1) {
2236 // cfg.cn[1].cls += ' pull-right'
2242 cls : 'dropdown-menu' ,
2243 style : 'z-index:1000'
2247 if (this.type === 'submenu') {
2248 cfg.cls = 'submenu active';
2250 if (this.type === 'treeview') {
2251 cfg.cls = 'treeview-menu';
2256 initEvents : function() {
2258 // Roo.log("ADD event");
2259 // Roo.log(this.triggerEl.dom);
2261 this.triggerEl.on('click', this.onTriggerClick, this);
2263 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2266 if (this.triggerEl.hasClass('nav-item')) {
2267 // dropdown toggle on the 'a' in BS4?
2268 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2270 this.triggerEl.addClass('dropdown-toggle');
2273 this.el.on('touchstart' , this.onTouch, this);
2275 this.el.on('click' , this.onClick, this);
2277 this.el.on("mouseover", this.onMouseOver, this);
2278 this.el.on("mouseout", this.onMouseOut, this);
2282 findTargetItem : function(e)
2284 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2288 //Roo.log(t); Roo.log(t.id);
2290 //Roo.log(this.menuitems);
2291 return this.menuitems.get(t.id);
2293 //return this.items.get(t.menuItemId);
2299 onTouch : function(e)
2301 Roo.log("menu.onTouch");
2302 //e.stopEvent(); this make the user popdown broken
2306 onClick : function(e)
2308 Roo.log("menu.onClick");
2310 var t = this.findTargetItem(e);
2311 if(!t || t.isContainer){
2316 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2317 if(t == this.activeItem && t.shouldDeactivate(e)){
2318 this.activeItem.deactivate();
2319 delete this.activeItem;
2323 this.setActiveItem(t, true);
2331 Roo.log('pass click event');
2335 this.fireEvent("click", this, t, e);
2339 if(!t.href.length || t.href == '#'){
2340 (function() { _this.hide(); }).defer(100);
2345 onMouseOver : function(e){
2346 var t = this.findTargetItem(e);
2349 // if(t.canActivate && !t.disabled){
2350 // this.setActiveItem(t, true);
2354 this.fireEvent("mouseover", this, e, t);
2356 isVisible : function(){
2357 return !this.hidden;
2359 onMouseOut : function(e){
2360 var t = this.findTargetItem(e);
2363 // if(t == this.activeItem && t.shouldDeactivate(e)){
2364 // this.activeItem.deactivate();
2365 // delete this.activeItem;
2368 this.fireEvent("mouseout", this, e, t);
2373 * Displays this menu relative to another element
2374 * @param {String/HTMLElement/Roo.Element} element The element to align to
2375 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2376 * the element (defaults to this.defaultAlign)
2377 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2379 show : function(el, pos, parentMenu)
2381 if (false === this.fireEvent("beforeshow", this)) {
2382 Roo.log("show canceled");
2385 this.parentMenu = parentMenu;
2390 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2393 * Displays this menu at a specific xy position
2394 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2395 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2397 showAt : function(xy, parentMenu, /* private: */_e){
2398 this.parentMenu = parentMenu;
2403 this.fireEvent("beforeshow", this);
2404 //xy = this.el.adjustForConstraints(xy);
2408 this.hideMenuItems();
2409 this.hidden = false;
2410 this.triggerEl.addClass('open');
2411 this.el.addClass('show');
2413 // reassign x when hitting right
2414 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2415 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2418 // reassign y when hitting bottom
2419 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2420 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2423 // but the list may align on trigger left or trigger top... should it be a properity?
2425 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2430 this.fireEvent("show", this);
2436 this.doFocus.defer(50, this);
2440 doFocus : function(){
2442 this.focusEl.focus();
2447 * Hides this menu and optionally all parent menus
2448 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2450 hide : function(deep)
2452 if (false === this.fireEvent("beforehide", this)) {
2453 Roo.log("hide canceled");
2456 this.hideMenuItems();
2457 if(this.el && this.isVisible()){
2459 if(this.activeItem){
2460 this.activeItem.deactivate();
2461 this.activeItem = null;
2463 this.triggerEl.removeClass('open');;
2464 this.el.removeClass('show');
2466 this.fireEvent("hide", this);
2468 if(deep === true && this.parentMenu){
2469 this.parentMenu.hide(true);
2473 onTriggerClick : function(e)
2475 Roo.log('trigger click');
2477 var target = e.getTarget();
2479 Roo.log(target.nodeName.toLowerCase());
2481 if(target.nodeName.toLowerCase() === 'i'){
2487 onTriggerPress : function(e)
2489 Roo.log('trigger press');
2490 //Roo.log(e.getTarget());
2491 // Roo.log(this.triggerEl.dom);
2493 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2494 var pel = Roo.get(e.getTarget());
2495 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2496 Roo.log('is treeview or dropdown?');
2500 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2504 if (this.isVisible()) {
2509 this.show(this.triggerEl, '?', false);
2512 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2519 hideMenuItems : function()
2521 Roo.log("hide Menu Items");
2526 this.el.select('.open',true).each(function(aa) {
2528 aa.removeClass('open');
2532 addxtypeChild : function (tree, cntr) {
2533 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2535 this.menuitems.add(comp);
2547 this.getEl().dom.innerHTML = '';
2548 this.menuitems.clear();
2562 * @class Roo.bootstrap.MenuItem
2563 * @extends Roo.bootstrap.Component
2564 * Bootstrap MenuItem class
2565 * @cfg {String} html the menu label
2566 * @cfg {String} href the link
2567 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2568 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2569 * @cfg {Boolean} active used on sidebars to highlight active itesm
2570 * @cfg {String} fa favicon to show on left of menu item.
2571 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2575 * Create a new MenuItem
2576 * @param {Object} config The config object
2580 Roo.bootstrap.MenuItem = function(config){
2581 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2586 * The raw click event for the entire grid.
2587 * @param {Roo.bootstrap.MenuItem} this
2588 * @param {Roo.EventObject} e
2594 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2598 preventDefault: false,
2599 isContainer : false,
2603 getAutoCreate : function(){
2605 if(this.isContainer){
2608 cls: 'dropdown-menu-item '
2618 cls : 'dropdown-item',
2623 if (this.fa !== false) {
2626 cls : 'fa fa-' + this.fa
2635 cls: 'dropdown-menu-item',
2638 if (this.parent().type == 'treeview') {
2639 cfg.cls = 'treeview-menu';
2642 cfg.cls += ' active';
2647 anc.href = this.href || cfg.cn[0].href ;
2648 ctag.html = this.html || cfg.cn[0].html ;
2652 initEvents: function()
2654 if (this.parent().type == 'treeview') {
2655 this.el.select('a').on('click', this.onClick, this);
2659 this.menu.parentType = this.xtype;
2660 this.menu.triggerEl = this.el;
2661 this.menu = this.addxtype(Roo.apply({}, this.menu));
2665 onClick : function(e)
2667 Roo.log('item on click ');
2669 if(this.preventDefault){
2672 //this.parent().hideMenuItems();
2674 this.fireEvent('click', this, e);
2693 * @class Roo.bootstrap.MenuSeparator
2694 * @extends Roo.bootstrap.Component
2695 * Bootstrap MenuSeparator class
2698 * Create a new MenuItem
2699 * @param {Object} config The config object
2703 Roo.bootstrap.MenuSeparator = function(config){
2704 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2707 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2709 getAutoCreate : function(){
2728 * @class Roo.bootstrap.Modal
2729 * @extends Roo.bootstrap.Component
2730 * Bootstrap Modal class
2731 * @cfg {String} title Title of dialog
2732 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2733 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2734 * @cfg {Boolean} specificTitle default false
2735 * @cfg {Array} buttons Array of buttons or standard button set..
2736 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2737 * @cfg {Boolean} animate default true
2738 * @cfg {Boolean} allow_close default true
2739 * @cfg {Boolean} fitwindow default false
2740 * @cfg {String} size (sm|lg) default empty
2741 * @cfg {Number} max_width set the max width of modal
2745 * Create a new Modal Dialog
2746 * @param {Object} config The config object
2749 Roo.bootstrap.Modal = function(config){
2750 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2755 * The raw btnclick event for the button
2756 * @param {Roo.EventObject} e
2761 * Fire when dialog resize
2762 * @param {Roo.bootstrap.Modal} this
2763 * @param {Roo.EventObject} e
2767 this.buttons = this.buttons || [];
2770 this.tmpl = Roo.factory(this.tmpl);
2775 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2777 title : 'test dialog',
2787 specificTitle: false,
2789 buttonPosition: 'right',
2812 onRender : function(ct, position)
2814 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2817 var cfg = Roo.apply({}, this.getAutoCreate());
2820 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2822 //if (!cfg.name.length) {
2826 cfg.cls += ' ' + this.cls;
2829 cfg.style = this.style;
2831 this.el = Roo.get(document.body).createChild(cfg, position);
2833 //var type = this.el.dom.type;
2836 if(this.tabIndex !== undefined){
2837 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2840 this.dialogEl = this.el.select('.modal-dialog',true).first();
2841 this.bodyEl = this.el.select('.modal-body',true).first();
2842 this.closeEl = this.el.select('.modal-header .close', true).first();
2843 this.headerEl = this.el.select('.modal-header',true).first();
2844 this.titleEl = this.el.select('.modal-title',true).first();
2845 this.footerEl = this.el.select('.modal-footer',true).first();
2847 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2849 //this.el.addClass("x-dlg-modal");
2851 if (this.buttons.length) {
2852 Roo.each(this.buttons, function(bb) {
2853 var b = Roo.apply({}, bb);
2854 b.xns = b.xns || Roo.bootstrap;
2855 b.xtype = b.xtype || 'Button';
2856 if (typeof(b.listeners) == 'undefined') {
2857 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2860 var btn = Roo.factory(b);
2862 btn.render(this.getButtonContainer());
2866 // render the children.
2869 if(typeof(this.items) != 'undefined'){
2870 var items = this.items;
2873 for(var i =0;i < items.length;i++) {
2874 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2878 this.items = nitems;
2880 // where are these used - they used to be body/close/footer
2884 //this.el.addClass([this.fieldClass, this.cls]);
2888 getAutoCreate : function()
2892 html : this.html || ''
2897 cls : 'modal-title',
2901 if(this.specificTitle){
2907 if (this.allow_close && Roo.bootstrap.version == 3) {
2917 if (this.allow_close && Roo.bootstrap.version == 4) {
2927 if(this.size.length){
2928 size = 'modal-' + this.size;
2931 var footer = Roo.bootstrap.version == 3 ?
2933 cls : 'modal-footer',
2937 cls: 'btn-' + this.buttonPosition
2942 { // BS4 uses mr-auto on left buttons....
2943 cls : 'modal-footer'
2954 cls: "modal-dialog " + size,
2957 cls : "modal-content",
2960 cls : 'modal-header',
2975 modal.cls += ' fade';
2981 getChildContainer : function() {
2986 getButtonContainer : function() {
2988 return Roo.bootstrap.version == 4 ?
2989 this.el.select('.modal-footer',true).first()
2990 : this.el.select('.modal-footer div',true).first();
2993 initEvents : function()
2995 if (this.allow_close) {
2996 this.closeEl.on('click', this.hide, this);
2998 Roo.EventManager.onWindowResize(this.resize, this, true);
3006 this.maskEl.setSize(
3007 Roo.lib.Dom.getViewWidth(true),
3008 Roo.lib.Dom.getViewHeight(true)
3011 if (this.fitwindow) {
3015 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
3016 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
3021 if(this.max_width !== 0) {
3023 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
3026 this.setSize(w, this.height);
3030 if(this.max_height) {
3031 this.setSize(w,Math.min(
3033 Roo.lib.Dom.getViewportHeight(true) - 60
3039 if(!this.fit_content) {
3040 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
3044 this.setSize(w, Math.min(
3046 this.headerEl.getHeight() +
3047 this.footerEl.getHeight() +
3048 this.getChildHeight(this.bodyEl.dom.childNodes),
3049 Roo.lib.Dom.getViewportHeight(true) - 60)
3055 setSize : function(w,h)
3066 if (!this.rendered) {
3070 //this.el.setStyle('display', 'block');
3071 this.el.removeClass('hideing');
3072 this.el.dom.style.display='block';
3074 Roo.get(document.body).addClass('modal-open');
3076 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
3079 this.el.addClass('show');
3080 this.el.addClass('in');
3083 this.el.addClass('show');
3084 this.el.addClass('in');
3087 // not sure how we can show data in here..
3089 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3092 Roo.get(document.body).addClass("x-body-masked");
3094 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3095 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3096 this.maskEl.dom.style.display = 'block';
3097 this.maskEl.addClass('show');
3102 this.fireEvent('show', this);
3104 // set zindex here - otherwise it appears to be ignored...
3105 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3108 this.items.forEach( function(e) {
3109 e.layout ? e.layout() : false;
3117 if(this.fireEvent("beforehide", this) !== false){
3119 this.maskEl.removeClass('show');
3121 this.maskEl.dom.style.display = '';
3122 Roo.get(document.body).removeClass("x-body-masked");
3123 this.el.removeClass('in');
3124 this.el.select('.modal-dialog', true).first().setStyle('transform','');
3126 if(this.animate){ // why
3127 this.el.addClass('hideing');
3128 this.el.removeClass('show');
3130 if (!this.el.hasClass('hideing')) {
3131 return; // it's been shown again...
3134 this.el.dom.style.display='';
3136 Roo.get(document.body).removeClass('modal-open');
3137 this.el.removeClass('hideing');
3141 this.el.removeClass('show');
3142 this.el.dom.style.display='';
3143 Roo.get(document.body).removeClass('modal-open');
3146 this.fireEvent('hide', this);
3149 isVisible : function()
3152 return this.el.hasClass('show') && !this.el.hasClass('hideing');
3156 addButton : function(str, cb)
3160 var b = Roo.apply({}, { html : str } );
3161 b.xns = b.xns || Roo.bootstrap;
3162 b.xtype = b.xtype || 'Button';
3163 if (typeof(b.listeners) == 'undefined') {
3164 b.listeners = { click : cb.createDelegate(this) };
3167 var btn = Roo.factory(b);
3169 btn.render(this.getButtonContainer());
3175 setDefaultButton : function(btn)
3177 //this.el.select('.modal-footer').()
3180 resizeTo: function(w,h)
3182 this.dialogEl.setWidth(w);
3184 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
3186 this.bodyEl.setHeight(h - diff);
3188 this.fireEvent('resize', this);
3191 setContentSize : function(w, h)
3195 onButtonClick: function(btn,e)
3198 this.fireEvent('btnclick', btn.name, e);
3201 * Set the title of the Dialog
3202 * @param {String} str new Title
3204 setTitle: function(str) {
3205 this.titleEl.dom.innerHTML = str;
3208 * Set the body of the Dialog
3209 * @param {String} str new Title
3211 setBody: function(str) {
3212 this.bodyEl.dom.innerHTML = str;
3215 * Set the body of the Dialog using the template
3216 * @param {Obj} data - apply this data to the template and replace the body contents.
3218 applyBody: function(obj)
3221 Roo.log("Error - using apply Body without a template");
3224 this.tmpl.overwrite(this.bodyEl, obj);
3227 getChildHeight : function(child_nodes)
3231 child_nodes.length == 0
3236 var child_height = 0;
3238 for(var i = 0; i < child_nodes.length; i++) {
3241 * for modal with tabs...
3242 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3244 var layout_childs = child_nodes[i].childNodes;
3246 for(var j = 0; j < layout_childs.length; j++) {
3248 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3250 var layout_body_childs = layout_childs[j].childNodes;
3252 for(var k = 0; k < layout_body_childs.length; k++) {
3254 if(layout_body_childs[k].classList.contains('navbar')) {
3255 child_height += layout_body_childs[k].offsetHeight;
3259 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3261 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3263 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3265 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3266 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3281 child_height += child_nodes[i].offsetHeight;
3282 // Roo.log(child_nodes[i].offsetHeight);
3285 return child_height;
3291 Roo.apply(Roo.bootstrap.Modal, {
3293 * Button config that displays a single OK button
3302 * Button config that displays Yes and No buttons
3318 * Button config that displays OK and Cancel buttons
3333 * Button config that displays Yes, No and Cancel buttons
3357 * messagebox - can be used as a replace
3361 * @class Roo.MessageBox
3362 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3366 Roo.Msg.alert('Status', 'Changes saved successfully.');
3368 // Prompt for user data:
3369 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3371 // process text value...
3375 // Show a dialog using config options:
3377 title:'Save Changes?',
3378 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3379 buttons: Roo.Msg.YESNOCANCEL,
3386 Roo.bootstrap.MessageBox = function(){
3387 var dlg, opt, mask, waitTimer;
3388 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3389 var buttons, activeTextEl, bwidth;
3393 var handleButton = function(button){
3395 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3399 var handleHide = function(){
3401 dlg.el.removeClass(opt.cls);
3404 // Roo.TaskMgr.stop(waitTimer);
3405 // waitTimer = null;
3410 var updateButtons = function(b){
3413 buttons["ok"].hide();
3414 buttons["cancel"].hide();
3415 buttons["yes"].hide();
3416 buttons["no"].hide();
3417 dlg.footerEl.hide();
3421 dlg.footerEl.show();
3422 for(var k in buttons){
3423 if(typeof buttons[k] != "function"){
3426 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3427 width += buttons[k].el.getWidth()+15;
3437 var handleEsc = function(d, k, e){
3438 if(opt && opt.closable !== false){
3448 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3449 * @return {Roo.BasicDialog} The BasicDialog element
3451 getDialog : function(){
3453 dlg = new Roo.bootstrap.Modal( {
3456 //constraintoviewport:false,
3458 //collapsible : false,
3463 //buttonAlign:"center",
3464 closeClick : function(){
3465 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3468 handleButton("cancel");
3473 dlg.on("hide", handleHide);
3475 //dlg.addKeyListener(27, handleEsc);
3477 this.buttons = buttons;
3478 var bt = this.buttonText;
3479 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3480 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3481 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3482 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3484 bodyEl = dlg.bodyEl.createChild({
3486 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3487 '<textarea class="roo-mb-textarea"></textarea>' +
3488 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3490 msgEl = bodyEl.dom.firstChild;
3491 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3492 textboxEl.enableDisplayMode();
3493 textboxEl.addKeyListener([10,13], function(){
3494 if(dlg.isVisible() && opt && opt.buttons){
3497 }else if(opt.buttons.yes){
3498 handleButton("yes");
3502 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3503 textareaEl.enableDisplayMode();
3504 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3505 progressEl.enableDisplayMode();
3507 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3508 var pf = progressEl.dom.firstChild;
3510 pp = Roo.get(pf.firstChild);
3511 pp.setHeight(pf.offsetHeight);
3519 * Updates the message box body text
3520 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3521 * the XHTML-compliant non-breaking space character '&#160;')
3522 * @return {Roo.MessageBox} This message box
3524 updateText : function(text)
3526 if(!dlg.isVisible() && !opt.width){
3527 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3528 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3530 msgEl.innerHTML = text || ' ';
3532 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3533 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3535 Math.min(opt.width || cw , this.maxWidth),
3536 Math.max(opt.minWidth || this.minWidth, bwidth)
3539 activeTextEl.setWidth(w);
3541 if(dlg.isVisible()){
3542 dlg.fixedcenter = false;
3544 // to big, make it scroll. = But as usual stupid IE does not support
3547 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3548 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3549 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3551 bodyEl.dom.style.height = '';
3552 bodyEl.dom.style.overflowY = '';
3555 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3557 bodyEl.dom.style.overflowX = '';
3560 dlg.setContentSize(w, bodyEl.getHeight());
3561 if(dlg.isVisible()){
3562 dlg.fixedcenter = true;
3568 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3569 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3570 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3571 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3572 * @return {Roo.MessageBox} This message box
3574 updateProgress : function(value, text){
3576 this.updateText(text);
3579 if (pp) { // weird bug on my firefox - for some reason this is not defined
3580 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3581 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3587 * Returns true if the message box is currently displayed
3588 * @return {Boolean} True if the message box is visible, else false
3590 isVisible : function(){
3591 return dlg && dlg.isVisible();
3595 * Hides the message box if it is displayed
3598 if(this.isVisible()){
3604 * Displays a new message box, or reinitializes an existing message box, based on the config options
3605 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3606 * The following config object properties are supported:
3608 Property Type Description
3609 ---------- --------------- ------------------------------------------------------------------------------------
3610 animEl String/Element An id or Element from which the message box should animate as it opens and
3611 closes (defaults to undefined)
3612 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3613 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3614 closable Boolean False to hide the top-right close button (defaults to true). Note that
3615 progress and wait dialogs will ignore this property and always hide the
3616 close button as they can only be closed programmatically.
3617 cls String A custom CSS class to apply to the message box element
3618 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3619 displayed (defaults to 75)
3620 fn Function A callback function to execute after closing the dialog. The arguments to the
3621 function will be btn (the name of the button that was clicked, if applicable,
3622 e.g. "ok"), and text (the value of the active text field, if applicable).
3623 Progress and wait dialogs will ignore this option since they do not respond to
3624 user actions and can only be closed programmatically, so any required function
3625 should be called by the same code after it closes the dialog.
3626 icon String A CSS class that provides a background image to be used as an icon for
3627 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3628 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3629 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3630 modal Boolean False to allow user interaction with the page while the message box is
3631 displayed (defaults to true)
3632 msg String A string that will replace the existing message box body text (defaults
3633 to the XHTML-compliant non-breaking space character ' ')
3634 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3635 progress Boolean True to display a progress bar (defaults to false)
3636 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3637 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3638 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3639 title String The title text
3640 value String The string value to set into the active textbox element if displayed
3641 wait Boolean True to display a progress bar (defaults to false)
3642 width Number The width of the dialog in pixels
3649 msg: 'Please enter your address:',
3651 buttons: Roo.MessageBox.OKCANCEL,
3654 animEl: 'addAddressBtn'
3657 * @param {Object} config Configuration options
3658 * @return {Roo.MessageBox} This message box
3660 show : function(options)
3663 // this causes nightmares if you show one dialog after another
3664 // especially on callbacks..
3666 if(this.isVisible()){
3669 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3670 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3671 Roo.log("New Dialog Message:" + options.msg )
3672 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3673 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3676 var d = this.getDialog();
3678 d.setTitle(opt.title || " ");
3679 d.closeEl.setDisplayed(opt.closable !== false);
3680 activeTextEl = textboxEl;
3681 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3686 textareaEl.setHeight(typeof opt.multiline == "number" ?
3687 opt.multiline : this.defaultTextHeight);
3688 activeTextEl = textareaEl;
3697 progressEl.setDisplayed(opt.progress === true);
3699 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3701 this.updateProgress(0);
3702 activeTextEl.dom.value = opt.value || "";
3704 dlg.setDefaultButton(activeTextEl);
3706 var bs = opt.buttons;
3710 }else if(bs && bs.yes){
3711 db = buttons["yes"];
3713 dlg.setDefaultButton(db);
3715 bwidth = updateButtons(opt.buttons);
3716 this.updateText(opt.msg);
3718 d.el.addClass(opt.cls);
3720 d.proxyDrag = opt.proxyDrag === true;
3721 d.modal = opt.modal !== false;
3722 d.mask = opt.modal !== false ? mask : false;
3724 // force it to the end of the z-index stack so it gets a cursor in FF
3725 document.body.appendChild(dlg.el.dom);
3726 d.animateTarget = null;
3727 d.show(options.animEl);
3733 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3734 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3735 * and closing the message box when the process is complete.
3736 * @param {String} title The title bar text
3737 * @param {String} msg The message box body text
3738 * @return {Roo.MessageBox} This message box
3740 progress : function(title, msg){
3747 minWidth: this.minProgressWidth,
3754 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3755 * If a callback function is passed it will be called after the user clicks the button, and the
3756 * id of the button that was clicked will be passed as the only parameter to the callback
3757 * (could also be the top-right close button).
3758 * @param {String} title The title bar text
3759 * @param {String} msg The message box body text
3760 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3761 * @param {Object} scope (optional) The scope of the callback function
3762 * @return {Roo.MessageBox} This message box
3764 alert : function(title, msg, fn, scope)
3779 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3780 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3781 * You are responsible for closing the message box when the process is complete.
3782 * @param {String} msg The message box body text
3783 * @param {String} title (optional) The title bar text
3784 * @return {Roo.MessageBox} This message box
3786 wait : function(msg, title){
3797 waitTimer = Roo.TaskMgr.start({
3799 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3807 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3808 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3809 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3810 * @param {String} title The title bar text
3811 * @param {String} msg The message box body text
3812 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3813 * @param {Object} scope (optional) The scope of the callback function
3814 * @return {Roo.MessageBox} This message box
3816 confirm : function(title, msg, fn, scope){
3820 buttons: this.YESNO,
3829 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3830 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3831 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3832 * (could also be the top-right close button) and the text that was entered will be passed as the two
3833 * parameters to the callback.
3834 * @param {String} title The title bar text
3835 * @param {String} msg The message box body text
3836 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3837 * @param {Object} scope (optional) The scope of the callback function
3838 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3839 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3840 * @return {Roo.MessageBox} This message box
3842 prompt : function(title, msg, fn, scope, multiline){
3846 buttons: this.OKCANCEL,
3851 multiline: multiline,
3858 * Button config that displays a single OK button
3863 * Button config that displays Yes and No buttons
3866 YESNO : {yes:true, no:true},
3868 * Button config that displays OK and Cancel buttons
3871 OKCANCEL : {ok:true, cancel:true},
3873 * Button config that displays Yes, No and Cancel buttons
3876 YESNOCANCEL : {yes:true, no:true, cancel:true},
3879 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3882 defaultTextHeight : 75,
3884 * The maximum width in pixels of the message box (defaults to 600)
3889 * The minimum width in pixels of the message box (defaults to 100)
3894 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3895 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3898 minProgressWidth : 250,
3900 * An object containing the default button text strings that can be overriden for localized language support.
3901 * Supported properties are: ok, cancel, yes and no.
3902 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3915 * Shorthand for {@link Roo.MessageBox}
3917 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3918 Roo.Msg = Roo.Msg || Roo.MessageBox;
3927 * @class Roo.bootstrap.Navbar
3928 * @extends Roo.bootstrap.Component
3929 * Bootstrap Navbar class
3932 * Create a new Navbar
3933 * @param {Object} config The config object
3937 Roo.bootstrap.Navbar = function(config){
3938 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3942 * @event beforetoggle
3943 * Fire before toggle the menu
3944 * @param {Roo.EventObject} e
3946 "beforetoggle" : true
3950 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3959 getAutoCreate : function(){
3962 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3966 initEvents :function ()
3968 //Roo.log(this.el.select('.navbar-toggle',true));
3969 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
3976 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3978 var size = this.el.getSize();
3979 this.maskEl.setSize(size.width, size.height);
3980 this.maskEl.enableDisplayMode("block");
3989 getChildContainer : function()
3991 if (this.el && this.el.select('.collapse').getCount()) {
3992 return this.el.select('.collapse',true).first();
4007 onToggle : function()
4010 if(this.fireEvent('beforetoggle', this) === false){
4013 var ce = this.el.select('.navbar-collapse',true).first();
4015 if (!ce.hasClass('show')) {
4025 * Expand the navbar pulldown
4027 expand : function ()
4030 var ce = this.el.select('.navbar-collapse',true).first();
4031 if (ce.hasClass('collapsing')) {
4034 ce.dom.style.height = '';
4036 ce.addClass('in'); // old...
4037 ce.removeClass('collapse');
4038 ce.addClass('show');
4039 var h = ce.getHeight();
4041 ce.removeClass('show');
4042 // at this point we should be able to see it..
4043 ce.addClass('collapsing');
4045 ce.setHeight(0); // resize it ...
4046 ce.on('transitionend', function() {
4047 //Roo.log('done transition');
4048 ce.removeClass('collapsing');
4049 ce.addClass('show');
4050 ce.removeClass('collapse');
4052 ce.dom.style.height = '';
4053 }, this, { single: true} );
4055 ce.dom.scrollTop = 0;
4058 * Collapse the navbar pulldown
4060 collapse : function()
4062 var ce = this.el.select('.navbar-collapse',true).first();
4064 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
4065 // it's collapsed or collapsing..
4068 ce.removeClass('in'); // old...
4069 ce.setHeight(ce.getHeight());
4070 ce.removeClass('show');
4071 ce.addClass('collapsing');
4073 ce.on('transitionend', function() {
4074 ce.dom.style.height = '';
4075 ce.removeClass('collapsing');
4076 ce.addClass('collapse');
4077 }, this, { single: true} );
4097 * @class Roo.bootstrap.NavSimplebar
4098 * @extends Roo.bootstrap.Navbar
4099 * Bootstrap Sidebar class
4101 * @cfg {Boolean} inverse is inverted color
4103 * @cfg {String} type (nav | pills | tabs)
4104 * @cfg {Boolean} arrangement stacked | justified
4105 * @cfg {String} align (left | right) alignment
4107 * @cfg {Boolean} main (true|false) main nav bar? default false
4108 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4110 * @cfg {String} tag (header|footer|nav|div) default is nav
4112 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4116 * Create a new Sidebar
4117 * @param {Object} config The config object
4121 Roo.bootstrap.NavSimplebar = function(config){
4122 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4125 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
4141 getAutoCreate : function(){
4145 tag : this.tag || 'div',
4146 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
4148 if (['light','white'].indexOf(this.weight) > -1) {
4149 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4151 cfg.cls += ' bg-' + this.weight;
4154 cfg.cls += ' navbar-inverse';
4158 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4160 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4169 cls: 'nav nav-' + this.xtype,
4175 this.type = this.type || 'nav';
4176 if (['tabs','pills'].indexOf(this.type) != -1) {
4177 cfg.cn[0].cls += ' nav-' + this.type
4181 if (this.type!=='nav') {
4182 Roo.log('nav type must be nav/tabs/pills')
4184 cfg.cn[0].cls += ' navbar-nav'
4190 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4191 cfg.cn[0].cls += ' nav-' + this.arrangement;
4195 if (this.align === 'right') {
4196 cfg.cn[0].cls += ' navbar-right';
4221 * navbar-expand-md fixed-top
4225 * @class Roo.bootstrap.NavHeaderbar
4226 * @extends Roo.bootstrap.NavSimplebar
4227 * Bootstrap Sidebar class
4229 * @cfg {String} brand what is brand
4230 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4231 * @cfg {String} brand_href href of the brand
4232 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4233 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4234 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4235 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4238 * Create a new Sidebar
4239 * @param {Object} config The config object
4243 Roo.bootstrap.NavHeaderbar = function(config){
4244 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4248 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4255 desktopCenter : false,
4258 getAutoCreate : function(){
4261 tag: this.nav || 'nav',
4262 cls: 'navbar navbar-expand-md',
4268 if (this.desktopCenter) {
4269 cn.push({cls : 'container', cn : []});
4277 cls: 'navbar-toggle navbar-toggler',
4278 'data-toggle': 'collapse',
4283 html: 'Toggle navigation'
4287 cls: 'icon-bar navbar-toggler-icon'
4300 cn.push( Roo.bootstrap.version == 4 ? btn : {
4302 cls: 'navbar-header',
4311 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
4315 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4317 if (['light','white'].indexOf(this.weight) > -1) {
4318 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4320 cfg.cls += ' bg-' + this.weight;
4323 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4324 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4326 // tag can override this..
4328 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4331 if (this.brand !== '') {
4332 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4333 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4335 href: this.brand_href ? this.brand_href : '#',
4336 cls: 'navbar-brand',
4344 cfg.cls += ' main-nav';
4352 getHeaderChildContainer : function()
4354 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4355 return this.el.select('.navbar-header',true).first();
4358 return this.getChildContainer();
4362 initEvents : function()
4364 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4366 if (this.autohide) {
4371 Roo.get(document).on('scroll',function(e) {
4372 var ns = Roo.get(document).getScroll().top;
4373 var os = prevScroll;
4377 ft.removeClass('slideDown');
4378 ft.addClass('slideUp');
4381 ft.removeClass('slideUp');
4382 ft.addClass('slideDown');
4403 * @class Roo.bootstrap.NavSidebar
4404 * @extends Roo.bootstrap.Navbar
4405 * Bootstrap Sidebar class
4408 * Create a new Sidebar
4409 * @param {Object} config The config object
4413 Roo.bootstrap.NavSidebar = function(config){
4414 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4417 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4419 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4421 getAutoCreate : function(){
4426 cls: 'sidebar sidebar-nav'
4448 * @class Roo.bootstrap.NavGroup
4449 * @extends Roo.bootstrap.Component
4450 * Bootstrap NavGroup class
4451 * @cfg {String} align (left|right)
4452 * @cfg {Boolean} inverse
4453 * @cfg {String} type (nav|pills|tab) default nav
4454 * @cfg {String} navId - reference Id for navbar.
4458 * Create a new nav group
4459 * @param {Object} config The config object
4462 Roo.bootstrap.NavGroup = function(config){
4463 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4466 Roo.bootstrap.NavGroup.register(this);
4470 * Fires when the active item changes
4471 * @param {Roo.bootstrap.NavGroup} this
4472 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4473 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4480 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4491 getAutoCreate : function()
4493 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4499 if (Roo.bootstrap.version == 4) {
4500 if (['tabs','pills'].indexOf(this.type) != -1) {
4501 cfg.cls += ' nav-' + this.type;
4503 // trying to remove so header bar can right align top?
4504 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
4505 // do not use on header bar...
4506 cfg.cls += ' navbar-nav';
4511 if (['tabs','pills'].indexOf(this.type) != -1) {
4512 cfg.cls += ' nav-' + this.type
4514 if (this.type !== 'nav') {
4515 Roo.log('nav type must be nav/tabs/pills')
4517 cfg.cls += ' navbar-nav'
4521 if (this.parent() && this.parent().sidebar) {
4524 cls: 'dashboard-menu sidebar-menu'
4530 if (this.form === true) {
4533 cls: 'navbar-form form-inline'
4535 //nav navbar-right ml-md-auto
4536 if (this.align === 'right') {
4537 cfg.cls += ' navbar-right ml-md-auto';
4539 cfg.cls += ' navbar-left';
4543 if (this.align === 'right') {
4544 cfg.cls += ' navbar-right ml-md-auto';
4546 cfg.cls += ' mr-auto';
4550 cfg.cls += ' navbar-inverse';
4558 * sets the active Navigation item
4559 * @param {Roo.bootstrap.NavItem} the new current navitem
4561 setActiveItem : function(item)
4564 Roo.each(this.navItems, function(v){
4569 v.setActive(false, true);
4576 item.setActive(true, true);
4577 this.fireEvent('changed', this, item, prev);
4582 * gets the active Navigation item
4583 * @return {Roo.bootstrap.NavItem} the current navitem
4585 getActive : function()
4589 Roo.each(this.navItems, function(v){
4600 indexOfNav : function()
4604 Roo.each(this.navItems, function(v,i){
4615 * adds a Navigation item
4616 * @param {Roo.bootstrap.NavItem} the navitem to add
4618 addItem : function(cfg)
4620 if (this.form && Roo.bootstrap.version == 4) {
4623 var cn = new Roo.bootstrap.NavItem(cfg);
4625 cn.parentId = this.id;
4626 cn.onRender(this.el, null);
4630 * register a Navigation item
4631 * @param {Roo.bootstrap.NavItem} the navitem to add
4633 register : function(item)
4635 this.navItems.push( item);
4636 item.navId = this.navId;
4641 * clear all the Navigation item
4644 clearAll : function()
4647 this.el.dom.innerHTML = '';
4650 getNavItem: function(tabId)
4653 Roo.each(this.navItems, function(e) {
4654 if (e.tabId == tabId) {
4664 setActiveNext : function()
4666 var i = this.indexOfNav(this.getActive());
4667 if (i > this.navItems.length) {
4670 this.setActiveItem(this.navItems[i+1]);
4672 setActivePrev : function()
4674 var i = this.indexOfNav(this.getActive());
4678 this.setActiveItem(this.navItems[i-1]);
4680 clearWasActive : function(except) {
4681 Roo.each(this.navItems, function(e) {
4682 if (e.tabId != except.tabId && e.was_active) {
4683 e.was_active = false;
4690 getWasActive : function ()
4693 Roo.each(this.navItems, function(e) {
4708 Roo.apply(Roo.bootstrap.NavGroup, {
4712 * register a Navigation Group
4713 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4715 register : function(navgrp)
4717 this.groups[navgrp.navId] = navgrp;
4721 * fetch a Navigation Group based on the navigation ID
4722 * @param {string} the navgroup to add
4723 * @returns {Roo.bootstrap.NavGroup} the navgroup
4725 get: function(navId) {
4726 if (typeof(this.groups[navId]) == 'undefined') {
4728 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4730 return this.groups[navId] ;
4745 * @class Roo.bootstrap.NavItem
4746 * @extends Roo.bootstrap.Component
4747 * Bootstrap Navbar.NavItem class
4748 * @cfg {String} href link to
4749 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4751 * @cfg {String} html content of button
4752 * @cfg {String} badge text inside badge
4753 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4754 * @cfg {String} glyphicon DEPRICATED - use fa
4755 * @cfg {String} icon DEPRICATED - use fa
4756 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4757 * @cfg {Boolean} active Is item active
4758 * @cfg {Boolean} disabled Is item disabled
4760 * @cfg {Boolean} preventDefault (true | false) default false
4761 * @cfg {String} tabId the tab that this item activates.
4762 * @cfg {String} tagtype (a|span) render as a href or span?
4763 * @cfg {Boolean} animateRef (true|false) link to element default false
4766 * Create a new Navbar Item
4767 * @param {Object} config The config object
4769 Roo.bootstrap.NavItem = function(config){
4770 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4775 * The raw click event for the entire grid.
4776 * @param {Roo.EventObject} e
4781 * Fires when the active item active state changes
4782 * @param {Roo.bootstrap.NavItem} this
4783 * @param {boolean} state the new state
4789 * Fires when scroll to element
4790 * @param {Roo.bootstrap.NavItem} this
4791 * @param {Object} options
4792 * @param {Roo.EventObject} e
4800 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4809 preventDefault : false,
4817 button_outline : false,
4821 getAutoCreate : function(){
4829 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4831 if (this.disabled) {
4832 cfg.cls += ' disabled';
4836 if (this.button_weight.length) {
4837 cfg.tag = this.href ? 'a' : 'button';
4838 cfg.html = this.html || '';
4839 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4841 cfg.href = this.href;
4844 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4847 // menu .. should add dropdown-menu class - so no need for carat..
4849 if (this.badge !== '') {
4851 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4856 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4860 href : this.href || "#",
4861 html: this.html || ''
4864 if (this.tagtype == 'a') {
4865 cfg.cn[0].cls = 'nav-link';
4868 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
4871 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
4873 if(this.glyphicon) {
4874 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4879 cfg.cn[0].html += " <span class='caret'></span>";
4883 if (this.badge !== '') {
4885 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4893 onRender : function(ct, position)
4895 // Roo.log("Call onRender: " + this.xtype);
4896 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4900 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4901 this.navLink = this.el.select('.nav-link',true).first();
4906 initEvents: function()
4908 if (typeof (this.menu) != 'undefined') {
4909 this.menu.parentType = this.xtype;
4910 this.menu.triggerEl = this.el;
4911 this.menu = this.addxtype(Roo.apply({}, this.menu));
4914 this.el.select('a',true).on('click', this.onClick, this);
4916 if(this.tagtype == 'span'){
4917 this.el.select('span',true).on('click', this.onClick, this);
4920 // at this point parent should be available..
4921 this.parent().register(this);
4924 onClick : function(e)
4926 if (e.getTarget('.dropdown-menu-item')) {
4927 // did you click on a menu itemm.... - then don't trigger onclick..
4932 this.preventDefault ||
4935 Roo.log("NavItem - prevent Default?");
4939 if (this.disabled) {
4943 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4944 if (tg && tg.transition) {
4945 Roo.log("waiting for the transitionend");
4951 //Roo.log("fire event clicked");
4952 if(this.fireEvent('click', this, e) === false){
4956 if(this.tagtype == 'span'){
4960 //Roo.log(this.href);
4961 var ael = this.el.select('a',true).first();
4964 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4965 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4966 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4967 return; // ignore... - it's a 'hash' to another page.
4969 Roo.log("NavItem - prevent Default?");
4971 this.scrollToElement(e);
4975 var p = this.parent();
4977 if (['tabs','pills'].indexOf(p.type)!==-1) {
4978 if (typeof(p.setActiveItem) !== 'undefined') {
4979 p.setActiveItem(this);
4983 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4984 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4985 // remove the collapsed menu expand...
4986 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
4990 isActive: function () {
4993 setActive : function(state, fire, is_was_active)
4995 if (this.active && !state && this.navId) {
4996 this.was_active = true;
4997 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4999 nv.clearWasActive(this);
5003 this.active = state;
5006 this.el.removeClass('active');
5007 this.navLink ? this.navLink.removeClass('active') : false;
5008 } else if (!this.el.hasClass('active')) {
5010 this.el.addClass('active');
5011 if (Roo.bootstrap.version == 4 && this.navLink ) {
5012 this.navLink.addClass('active');
5017 this.fireEvent('changed', this, state);
5020 // show a panel if it's registered and related..
5022 if (!this.navId || !this.tabId || !state || is_was_active) {
5026 var tg = Roo.bootstrap.TabGroup.get(this.navId);
5030 var pan = tg.getPanelByName(this.tabId);
5034 // if we can not flip to new panel - go back to old nav highlight..
5035 if (false == tg.showPanel(pan)) {
5036 var nv = Roo.bootstrap.NavGroup.get(this.navId);
5038 var onav = nv.getWasActive();
5040 onav.setActive(true, false, true);
5049 // this should not be here...
5050 setDisabled : function(state)
5052 this.disabled = state;
5054 this.el.removeClass('disabled');
5055 } else if (!this.el.hasClass('disabled')) {
5056 this.el.addClass('disabled');
5062 * Fetch the element to display the tooltip on.
5063 * @return {Roo.Element} defaults to this.el
5065 tooltipEl : function()
5067 return this.el.select('' + this.tagtype + '', true).first();
5070 scrollToElement : function(e)
5072 var c = document.body;
5075 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
5077 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
5078 c = document.documentElement;
5081 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
5087 var o = target.calcOffsetsTo(c);
5094 this.fireEvent('scrollto', this, options, e);
5096 Roo.get(c).scrollTo('top', options.value, true);
5109 * <span> icon </span>
5110 * <span> text </span>
5111 * <span>badge </span>
5115 * @class Roo.bootstrap.NavSidebarItem
5116 * @extends Roo.bootstrap.NavItem
5117 * Bootstrap Navbar.NavSidebarItem class
5118 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5119 * {Boolean} open is the menu open
5120 * {Boolean} buttonView use button as the tigger el rather that a (default false)
5121 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5122 * {String} buttonSize (sm|md|lg)the extra classes for the button
5123 * {Boolean} showArrow show arrow next to the text (default true)
5125 * Create a new Navbar Button
5126 * @param {Object} config The config object
5128 Roo.bootstrap.NavSidebarItem = function(config){
5129 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5134 * The raw click event for the entire grid.
5135 * @param {Roo.EventObject} e
5140 * Fires when the active item active state changes
5141 * @param {Roo.bootstrap.NavSidebarItem} this
5142 * @param {boolean} state the new state
5150 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
5152 badgeWeight : 'default',
5158 buttonWeight : 'default',
5164 getAutoCreate : function(){
5169 href : this.href || '#',
5175 if(this.buttonView){
5178 href : this.href || '#',
5179 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5192 cfg.cls += ' active';
5195 if (this.disabled) {
5196 cfg.cls += ' disabled';
5199 cfg.cls += ' open x-open';
5202 if (this.glyphicon || this.icon) {
5203 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
5204 a.cn.push({ tag : 'i', cls : c }) ;
5207 if(!this.buttonView){
5210 html : this.html || ''
5217 if (this.badge !== '') {
5218 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
5224 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5227 a.cls += ' dropdown-toggle treeview' ;
5233 initEvents : function()
5235 if (typeof (this.menu) != 'undefined') {
5236 this.menu.parentType = this.xtype;
5237 this.menu.triggerEl = this.el;
5238 this.menu = this.addxtype(Roo.apply({}, this.menu));
5241 this.el.on('click', this.onClick, this);
5243 if(this.badge !== ''){
5244 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5249 onClick : function(e)
5256 if(this.preventDefault){
5260 this.fireEvent('click', this, e);
5263 disable : function()
5265 this.setDisabled(true);
5270 this.setDisabled(false);
5273 setDisabled : function(state)
5275 if(this.disabled == state){
5279 this.disabled = state;
5282 this.el.addClass('disabled');
5286 this.el.removeClass('disabled');
5291 setActive : function(state)
5293 if(this.active == state){
5297 this.active = state;
5300 this.el.addClass('active');
5304 this.el.removeClass('active');
5309 isActive: function ()
5314 setBadge : function(str)
5320 this.badgeEl.dom.innerHTML = str;
5337 * @class Roo.bootstrap.Row
5338 * @extends Roo.bootstrap.Component
5339 * Bootstrap Row class (contains columns...)
5343 * @param {Object} config The config object
5346 Roo.bootstrap.Row = function(config){
5347 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5350 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5352 getAutoCreate : function(){
5371 * @class Roo.bootstrap.Element
5372 * @extends Roo.bootstrap.Component
5373 * Bootstrap Element class
5374 * @cfg {String} html contents of the element
5375 * @cfg {String} tag tag of the element
5376 * @cfg {String} cls class of the element
5377 * @cfg {Boolean} preventDefault (true|false) default false
5378 * @cfg {Boolean} clickable (true|false) default false
5381 * Create a new Element
5382 * @param {Object} config The config object
5385 Roo.bootstrap.Element = function(config){
5386 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5392 * When a element is chick
5393 * @param {Roo.bootstrap.Element} this
5394 * @param {Roo.EventObject} e
5400 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5405 preventDefault: false,
5408 getAutoCreate : function(){
5412 // cls: this.cls, double assign in parent class Component.js :: onRender
5419 initEvents: function()
5421 Roo.bootstrap.Element.superclass.initEvents.call(this);
5424 this.el.on('click', this.onClick, this);
5429 onClick : function(e)
5431 if(this.preventDefault){
5435 this.fireEvent('click', this, e);
5438 getValue : function()
5440 return this.el.dom.innerHTML;
5443 setValue : function(value)
5445 this.el.dom.innerHTML = value;
5460 * @class Roo.bootstrap.Pagination
5461 * @extends Roo.bootstrap.Component
5462 * Bootstrap Pagination class
5463 * @cfg {String} size xs | sm | md | lg
5464 * @cfg {Boolean} inverse false | true
5467 * Create a new Pagination
5468 * @param {Object} config The config object
5471 Roo.bootstrap.Pagination = function(config){
5472 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5475 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5481 getAutoCreate : function(){
5487 cfg.cls += ' inverse';
5493 cfg.cls += " " + this.cls;
5511 * @class Roo.bootstrap.PaginationItem
5512 * @extends Roo.bootstrap.Component
5513 * Bootstrap PaginationItem class
5514 * @cfg {String} html text
5515 * @cfg {String} href the link
5516 * @cfg {Boolean} preventDefault (true | false) default true
5517 * @cfg {Boolean} active (true | false) default false
5518 * @cfg {Boolean} disabled default false
5522 * Create a new PaginationItem
5523 * @param {Object} config The config object
5527 Roo.bootstrap.PaginationItem = function(config){
5528 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5533 * The raw click event for the entire grid.
5534 * @param {Roo.EventObject} e
5540 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5544 preventDefault: true,
5549 getAutoCreate : function(){
5555 href : this.href ? this.href : '#',
5556 html : this.html ? this.html : ''
5566 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5570 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5576 initEvents: function() {
5578 this.el.on('click', this.onClick, this);
5581 onClick : function(e)
5583 Roo.log('PaginationItem on click ');
5584 if(this.preventDefault){
5592 this.fireEvent('click', this, e);
5608 * @class Roo.bootstrap.Slider
5609 * @extends Roo.bootstrap.Component
5610 * Bootstrap Slider class
5613 * Create a new Slider
5614 * @param {Object} config The config object
5617 Roo.bootstrap.Slider = function(config){
5618 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5621 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5623 getAutoCreate : function(){
5627 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5631 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5643 * Ext JS Library 1.1.1
5644 * Copyright(c) 2006-2007, Ext JS, LLC.
5646 * Originally Released Under LGPL - original licence link has changed is not relivant.
5649 * <script type="text/javascript">
5654 * @class Roo.grid.ColumnModel
5655 * @extends Roo.util.Observable
5656 * This is the default implementation of a ColumnModel used by the Grid. It defines
5657 * the columns in the grid.
5660 var colModel = new Roo.grid.ColumnModel([
5661 {header: "Ticker", width: 60, sortable: true, locked: true},
5662 {header: "Company Name", width: 150, sortable: true},
5663 {header: "Market Cap.", width: 100, sortable: true},
5664 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5665 {header: "Employees", width: 100, sortable: true, resizable: false}
5670 * The config options listed for this class are options which may appear in each
5671 * individual column definition.
5672 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5674 * @param {Object} config An Array of column config objects. See this class's
5675 * config objects for details.
5677 Roo.grid.ColumnModel = function(config){
5679 * The config passed into the constructor
5681 this.config = config;
5684 // if no id, create one
5685 // if the column does not have a dataIndex mapping,
5686 // map it to the order it is in the config
5687 for(var i = 0, len = config.length; i < len; i++){
5689 if(typeof c.dataIndex == "undefined"){
5692 if(typeof c.renderer == "string"){
5693 c.renderer = Roo.util.Format[c.renderer];
5695 if(typeof c.id == "undefined"){
5698 if(c.editor && c.editor.xtype){
5699 c.editor = Roo.factory(c.editor, Roo.grid);
5701 if(c.editor && c.editor.isFormField){
5702 c.editor = new Roo.grid.GridEditor(c.editor);
5704 this.lookup[c.id] = c;
5708 * The width of columns which have no width specified (defaults to 100)
5711 this.defaultWidth = 100;
5714 * Default sortable of columns which have no sortable specified (defaults to false)
5717 this.defaultSortable = false;
5721 * @event widthchange
5722 * Fires when the width of a column changes.
5723 * @param {ColumnModel} this
5724 * @param {Number} columnIndex The column index
5725 * @param {Number} newWidth The new width
5727 "widthchange": true,
5729 * @event headerchange
5730 * Fires when the text of a header changes.
5731 * @param {ColumnModel} this
5732 * @param {Number} columnIndex The column index
5733 * @param {Number} newText The new header text
5735 "headerchange": true,
5737 * @event hiddenchange
5738 * Fires when a column is hidden or "unhidden".
5739 * @param {ColumnModel} this
5740 * @param {Number} columnIndex The column index
5741 * @param {Boolean} hidden true if hidden, false otherwise
5743 "hiddenchange": true,
5745 * @event columnmoved
5746 * Fires when a column is moved.
5747 * @param {ColumnModel} this
5748 * @param {Number} oldIndex
5749 * @param {Number} newIndex
5751 "columnmoved" : true,
5753 * @event columlockchange
5754 * Fires when a column's locked state is changed
5755 * @param {ColumnModel} this
5756 * @param {Number} colIndex
5757 * @param {Boolean} locked true if locked
5759 "columnlockchange" : true
5761 Roo.grid.ColumnModel.superclass.constructor.call(this);
5763 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5765 * @cfg {String} header The header text to display in the Grid view.
5768 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5769 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5770 * specified, the column's index is used as an index into the Record's data Array.
5773 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5774 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5777 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5778 * Defaults to the value of the {@link #defaultSortable} property.
5779 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5782 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5785 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5788 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5791 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5794 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5795 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5796 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5797 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5800 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5803 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5806 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5809 * @cfg {String} cursor (Optional)
5812 * @cfg {String} tooltip (Optional)
5815 * @cfg {Number} xs (Optional)
5818 * @cfg {Number} sm (Optional)
5821 * @cfg {Number} md (Optional)
5824 * @cfg {Number} lg (Optional)
5827 * Returns the id of the column at the specified index.
5828 * @param {Number} index The column index
5829 * @return {String} the id
5831 getColumnId : function(index){
5832 return this.config[index].id;
5836 * Returns the column for a specified id.
5837 * @param {String} id The column id
5838 * @return {Object} the column
5840 getColumnById : function(id){
5841 return this.lookup[id];
5846 * Returns the column for a specified dataIndex.
5847 * @param {String} dataIndex The column dataIndex
5848 * @return {Object|Boolean} the column or false if not found
5850 getColumnByDataIndex: function(dataIndex){
5851 var index = this.findColumnIndex(dataIndex);
5852 return index > -1 ? this.config[index] : false;
5856 * Returns the index for a specified column id.
5857 * @param {String} id The column id
5858 * @return {Number} the index, or -1 if not found
5860 getIndexById : function(id){
5861 for(var i = 0, len = this.config.length; i < len; i++){
5862 if(this.config[i].id == id){
5870 * Returns the index for a specified column dataIndex.
5871 * @param {String} dataIndex The column dataIndex
5872 * @return {Number} the index, or -1 if not found
5875 findColumnIndex : function(dataIndex){
5876 for(var i = 0, len = this.config.length; i < len; i++){
5877 if(this.config[i].dataIndex == dataIndex){
5885 moveColumn : function(oldIndex, newIndex){
5886 var c = this.config[oldIndex];
5887 this.config.splice(oldIndex, 1);
5888 this.config.splice(newIndex, 0, c);
5889 this.dataMap = null;
5890 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5893 isLocked : function(colIndex){
5894 return this.config[colIndex].locked === true;
5897 setLocked : function(colIndex, value, suppressEvent){
5898 if(this.isLocked(colIndex) == value){
5901 this.config[colIndex].locked = value;
5903 this.fireEvent("columnlockchange", this, colIndex, value);
5907 getTotalLockedWidth : function(){
5909 for(var i = 0; i < this.config.length; i++){
5910 if(this.isLocked(i) && !this.isHidden(i)){
5911 this.totalWidth += this.getColumnWidth(i);
5917 getLockedCount : function(){
5918 for(var i = 0, len = this.config.length; i < len; i++){
5919 if(!this.isLocked(i)){
5924 return this.config.length;
5928 * Returns the number of columns.
5931 getColumnCount : function(visibleOnly){
5932 if(visibleOnly === true){
5934 for(var i = 0, len = this.config.length; i < len; i++){
5935 if(!this.isHidden(i)){
5941 return this.config.length;
5945 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5946 * @param {Function} fn
5947 * @param {Object} scope (optional)
5948 * @return {Array} result
5950 getColumnsBy : function(fn, scope){
5952 for(var i = 0, len = this.config.length; i < len; i++){
5953 var c = this.config[i];
5954 if(fn.call(scope||this, c, i) === true){
5962 * Returns true if the specified column is sortable.
5963 * @param {Number} col The column index
5966 isSortable : function(col){
5967 if(typeof this.config[col].sortable == "undefined"){
5968 return this.defaultSortable;
5970 return this.config[col].sortable;
5974 * Returns the rendering (formatting) function defined for the column.
5975 * @param {Number} col The column index.
5976 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5978 getRenderer : function(col){
5979 if(!this.config[col].renderer){
5980 return Roo.grid.ColumnModel.defaultRenderer;
5982 return this.config[col].renderer;
5986 * Sets the rendering (formatting) function for a column.
5987 * @param {Number} col The column index
5988 * @param {Function} fn The function to use to process the cell's raw data
5989 * to return HTML markup for the grid view. The render function is called with
5990 * the following parameters:<ul>
5991 * <li>Data value.</li>
5992 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5993 * <li>css A CSS style string to apply to the table cell.</li>
5994 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5995 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5996 * <li>Row index</li>
5997 * <li>Column index</li>
5998 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
6000 setRenderer : function(col, fn){
6001 this.config[col].renderer = fn;
6005 * Returns the width for the specified column.
6006 * @param {Number} col The column index
6009 getColumnWidth : function(col){
6010 return this.config[col].width * 1 || this.defaultWidth;
6014 * Sets the width for a column.
6015 * @param {Number} col The column index
6016 * @param {Number} width The new width
6018 setColumnWidth : function(col, width, suppressEvent){
6019 this.config[col].width = width;
6020 this.totalWidth = null;
6022 this.fireEvent("widthchange", this, col, width);
6027 * Returns the total width of all columns.
6028 * @param {Boolean} includeHidden True to include hidden column widths
6031 getTotalWidth : function(includeHidden){
6032 if(!this.totalWidth){
6033 this.totalWidth = 0;
6034 for(var i = 0, len = this.config.length; i < len; i++){
6035 if(includeHidden || !this.isHidden(i)){
6036 this.totalWidth += this.getColumnWidth(i);
6040 return this.totalWidth;
6044 * Returns the header for the specified column.
6045 * @param {Number} col The column index
6048 getColumnHeader : function(col){
6049 return this.config[col].header;
6053 * Sets the header for a column.
6054 * @param {Number} col The column index
6055 * @param {String} header The new header
6057 setColumnHeader : function(col, header){
6058 this.config[col].header = header;
6059 this.fireEvent("headerchange", this, col, header);
6063 * Returns the tooltip for the specified column.
6064 * @param {Number} col The column index
6067 getColumnTooltip : function(col){
6068 return this.config[col].tooltip;
6071 * Sets the tooltip for a column.
6072 * @param {Number} col The column index
6073 * @param {String} tooltip The new tooltip
6075 setColumnTooltip : function(col, tooltip){
6076 this.config[col].tooltip = tooltip;
6080 * Returns the dataIndex for the specified column.
6081 * @param {Number} col The column index
6084 getDataIndex : function(col){
6085 return this.config[col].dataIndex;
6089 * Sets the dataIndex for a column.
6090 * @param {Number} col The column index
6091 * @param {Number} dataIndex The new dataIndex
6093 setDataIndex : function(col, dataIndex){
6094 this.config[col].dataIndex = dataIndex;
6100 * Returns true if the cell is editable.
6101 * @param {Number} colIndex The column index
6102 * @param {Number} rowIndex The row index - this is nto actually used..?
6105 isCellEditable : function(colIndex, rowIndex){
6106 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6110 * Returns the editor defined for the cell/column.
6111 * return false or null to disable editing.
6112 * @param {Number} colIndex The column index
6113 * @param {Number} rowIndex The row index
6116 getCellEditor : function(colIndex, rowIndex){
6117 return this.config[colIndex].editor;
6121 * Sets if a column is editable.
6122 * @param {Number} col The column index
6123 * @param {Boolean} editable True if the column is editable
6125 setEditable : function(col, editable){
6126 this.config[col].editable = editable;
6131 * Returns true if the column is hidden.
6132 * @param {Number} colIndex The column index
6135 isHidden : function(colIndex){
6136 return this.config[colIndex].hidden;
6141 * Returns true if the column width cannot be changed
6143 isFixed : function(colIndex){
6144 return this.config[colIndex].fixed;
6148 * Returns true if the column can be resized
6151 isResizable : function(colIndex){
6152 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6155 * Sets if a column is hidden.
6156 * @param {Number} colIndex The column index
6157 * @param {Boolean} hidden True if the column is hidden
6159 setHidden : function(colIndex, hidden){
6160 this.config[colIndex].hidden = hidden;
6161 this.totalWidth = null;
6162 this.fireEvent("hiddenchange", this, colIndex, hidden);
6166 * Sets the editor for a column.
6167 * @param {Number} col The column index
6168 * @param {Object} editor The editor object
6170 setEditor : function(col, editor){
6171 this.config[col].editor = editor;
6175 Roo.grid.ColumnModel.defaultRenderer = function(value)
6177 if(typeof value == "object") {
6180 if(typeof value == "string" && value.length < 1){
6184 return String.format("{0}", value);
6187 // Alias for backwards compatibility
6188 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6191 * Ext JS Library 1.1.1
6192 * Copyright(c) 2006-2007, Ext JS, LLC.
6194 * Originally Released Under LGPL - original licence link has changed is not relivant.
6197 * <script type="text/javascript">
6201 * @class Roo.LoadMask
6202 * A simple utility class for generically masking elements while loading data. If the element being masked has
6203 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6204 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
6205 * element's UpdateManager load indicator and will be destroyed after the initial load.
6207 * Create a new LoadMask
6208 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6209 * @param {Object} config The config object
6211 Roo.LoadMask = function(el, config){
6212 this.el = Roo.get(el);
6213 Roo.apply(this, config);
6215 this.store.on('beforeload', this.onBeforeLoad, this);
6216 this.store.on('load', this.onLoad, this);
6217 this.store.on('loadexception', this.onLoadException, this);
6218 this.removeMask = false;
6220 var um = this.el.getUpdateManager();
6221 um.showLoadIndicator = false; // disable the default indicator
6222 um.on('beforeupdate', this.onBeforeLoad, this);
6223 um.on('update', this.onLoad, this);
6224 um.on('failure', this.onLoad, this);
6225 this.removeMask = true;
6229 Roo.LoadMask.prototype = {
6231 * @cfg {Boolean} removeMask
6232 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6233 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
6237 * The text to display in a centered loading message box (defaults to 'Loading...')
6241 * @cfg {String} msgCls
6242 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6244 msgCls : 'x-mask-loading',
6247 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6253 * Disables the mask to prevent it from being displayed
6255 disable : function(){
6256 this.disabled = true;
6260 * Enables the mask so that it can be displayed
6262 enable : function(){
6263 this.disabled = false;
6266 onLoadException : function()
6270 if (typeof(arguments[3]) != 'undefined') {
6271 Roo.MessageBox.alert("Error loading",arguments[3]);
6275 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6276 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6283 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6288 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6292 onBeforeLoad : function(){
6294 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6299 destroy : function(){
6301 this.store.un('beforeload', this.onBeforeLoad, this);
6302 this.store.un('load', this.onLoad, this);
6303 this.store.un('loadexception', this.onLoadException, this);
6305 var um = this.el.getUpdateManager();
6306 um.un('beforeupdate', this.onBeforeLoad, this);
6307 um.un('update', this.onLoad, this);
6308 um.un('failure', this.onLoad, this);
6319 * @class Roo.bootstrap.Table
6320 * @extends Roo.bootstrap.Component
6321 * Bootstrap Table class
6322 * @cfg {String} cls table class
6323 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6324 * @cfg {String} bgcolor Specifies the background color for a table
6325 * @cfg {Number} border Specifies whether the table cells should have borders or not
6326 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6327 * @cfg {Number} cellspacing Specifies the space between cells
6328 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6329 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6330 * @cfg {String} sortable Specifies that the table should be sortable
6331 * @cfg {String} summary Specifies a summary of the content of a table
6332 * @cfg {Number} width Specifies the width of a table
6333 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6335 * @cfg {boolean} striped Should the rows be alternative striped
6336 * @cfg {boolean} bordered Add borders to the table
6337 * @cfg {boolean} hover Add hover highlighting
6338 * @cfg {boolean} condensed Format condensed
6339 * @cfg {boolean} responsive Format condensed
6340 * @cfg {Boolean} loadMask (true|false) default false
6341 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6342 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6343 * @cfg {Boolean} rowSelection (true|false) default false
6344 * @cfg {Boolean} cellSelection (true|false) default false
6345 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6346 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6347 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6348 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6352 * Create a new Table
6353 * @param {Object} config The config object
6356 Roo.bootstrap.Table = function(config){
6357 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6362 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6363 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6364 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6365 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6367 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6369 this.sm.grid = this;
6370 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6371 this.sm = this.selModel;
6372 this.sm.xmodule = this.xmodule || false;
6375 if (this.cm && typeof(this.cm.config) == 'undefined') {
6376 this.colModel = new Roo.grid.ColumnModel(this.cm);
6377 this.cm = this.colModel;
6378 this.cm.xmodule = this.xmodule || false;
6381 this.store= Roo.factory(this.store, Roo.data);
6382 this.ds = this.store;
6383 this.ds.xmodule = this.xmodule || false;
6386 if (this.footer && this.store) {
6387 this.footer.dataSource = this.ds;
6388 this.footer = Roo.factory(this.footer);
6395 * Fires when a cell is clicked
6396 * @param {Roo.bootstrap.Table} this
6397 * @param {Roo.Element} el
6398 * @param {Number} rowIndex
6399 * @param {Number} columnIndex
6400 * @param {Roo.EventObject} e
6404 * @event celldblclick
6405 * Fires when a cell is double clicked
6406 * @param {Roo.bootstrap.Table} this
6407 * @param {Roo.Element} el
6408 * @param {Number} rowIndex
6409 * @param {Number} columnIndex
6410 * @param {Roo.EventObject} e
6412 "celldblclick" : true,
6415 * Fires when a row is clicked
6416 * @param {Roo.bootstrap.Table} this
6417 * @param {Roo.Element} el
6418 * @param {Number} rowIndex
6419 * @param {Roo.EventObject} e
6423 * @event rowdblclick
6424 * Fires when a row is double clicked
6425 * @param {Roo.bootstrap.Table} this
6426 * @param {Roo.Element} el
6427 * @param {Number} rowIndex
6428 * @param {Roo.EventObject} e
6430 "rowdblclick" : true,
6433 * Fires when a mouseover occur
6434 * @param {Roo.bootstrap.Table} this
6435 * @param {Roo.Element} el
6436 * @param {Number} rowIndex
6437 * @param {Number} columnIndex
6438 * @param {Roo.EventObject} e
6443 * Fires when a mouseout occur
6444 * @param {Roo.bootstrap.Table} this
6445 * @param {Roo.Element} el
6446 * @param {Number} rowIndex
6447 * @param {Number} columnIndex
6448 * @param {Roo.EventObject} e
6453 * Fires when a row is rendered, so you can change add a style to it.
6454 * @param {Roo.bootstrap.Table} this
6455 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6459 * @event rowsrendered
6460 * Fires when all the rows have been rendered
6461 * @param {Roo.bootstrap.Table} this
6463 'rowsrendered' : true,
6465 * @event contextmenu
6466 * The raw contextmenu event for the entire grid.
6467 * @param {Roo.EventObject} e
6469 "contextmenu" : true,
6471 * @event rowcontextmenu
6472 * Fires when a row is right clicked
6473 * @param {Roo.bootstrap.Table} this
6474 * @param {Number} rowIndex
6475 * @param {Roo.EventObject} e
6477 "rowcontextmenu" : true,
6479 * @event cellcontextmenu
6480 * Fires when a cell is right clicked
6481 * @param {Roo.bootstrap.Table} this
6482 * @param {Number} rowIndex
6483 * @param {Number} cellIndex
6484 * @param {Roo.EventObject} e
6486 "cellcontextmenu" : true,
6488 * @event headercontextmenu
6489 * Fires when a header is right clicked
6490 * @param {Roo.bootstrap.Table} this
6491 * @param {Number} columnIndex
6492 * @param {Roo.EventObject} e
6494 "headercontextmenu" : true
6498 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6524 rowSelection : false,
6525 cellSelection : false,
6528 // Roo.Element - the tbody
6530 // Roo.Element - thead element
6533 container: false, // used by gridpanel...
6539 auto_hide_footer : false,
6541 getAutoCreate : function()
6543 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6550 if (this.scrollBody) {
6551 cfg.cls += ' table-body-fixed';
6554 cfg.cls += ' table-striped';
6558 cfg.cls += ' table-hover';
6560 if (this.bordered) {
6561 cfg.cls += ' table-bordered';
6563 if (this.condensed) {
6564 cfg.cls += ' table-condensed';
6566 if (this.responsive) {
6567 cfg.cls += ' table-responsive';
6571 cfg.cls+= ' ' +this.cls;
6574 // this lot should be simplifed...
6587 ].forEach(function(k) {
6595 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6598 if(this.store || this.cm){
6599 if(this.headerShow){
6600 cfg.cn.push(this.renderHeader());
6603 cfg.cn.push(this.renderBody());
6605 if(this.footerShow){
6606 cfg.cn.push(this.renderFooter());
6608 // where does this come from?
6609 //cfg.cls+= ' TableGrid';
6612 return { cn : [ cfg ] };
6615 initEvents : function()
6617 if(!this.store || !this.cm){
6620 if (this.selModel) {
6621 this.selModel.initEvents();
6625 //Roo.log('initEvents with ds!!!!');
6627 this.mainBody = this.el.select('tbody', true).first();
6628 this.mainHead = this.el.select('thead', true).first();
6629 this.mainFoot = this.el.select('tfoot', true).first();
6635 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6636 e.on('click', _this.sort, _this);
6639 this.mainBody.on("click", this.onClick, this);
6640 this.mainBody.on("dblclick", this.onDblClick, this);
6642 // why is this done????? = it breaks dialogs??
6643 //this.parent().el.setStyle('position', 'relative');
6647 this.footer.parentId = this.id;
6648 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6651 this.el.select('tfoot tr td').first().addClass('hide');
6656 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6659 this.store.on('load', this.onLoad, this);
6660 this.store.on('beforeload', this.onBeforeLoad, this);
6661 this.store.on('update', this.onUpdate, this);
6662 this.store.on('add', this.onAdd, this);
6663 this.store.on("clear", this.clear, this);
6665 this.el.on("contextmenu", this.onContextMenu, this);
6667 this.mainBody.on('scroll', this.onBodyScroll, this);
6669 this.cm.on("headerchange", this.onHeaderChange, this);
6671 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6675 onContextMenu : function(e, t)
6677 this.processEvent("contextmenu", e);
6680 processEvent : function(name, e)
6682 if (name != 'touchstart' ) {
6683 this.fireEvent(name, e);
6686 var t = e.getTarget();
6688 var cell = Roo.get(t);
6694 if(cell.findParent('tfoot', false, true)){
6698 if(cell.findParent('thead', false, true)){
6700 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6701 cell = Roo.get(t).findParent('th', false, true);
6703 Roo.log("failed to find th in thead?");
6704 Roo.log(e.getTarget());
6709 var cellIndex = cell.dom.cellIndex;
6711 var ename = name == 'touchstart' ? 'click' : name;
6712 this.fireEvent("header" + ename, this, cellIndex, e);
6717 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6718 cell = Roo.get(t).findParent('td', false, true);
6720 Roo.log("failed to find th in tbody?");
6721 Roo.log(e.getTarget());
6726 var row = cell.findParent('tr', false, true);
6727 var cellIndex = cell.dom.cellIndex;
6728 var rowIndex = row.dom.rowIndex - 1;
6732 this.fireEvent("row" + name, this, rowIndex, e);
6736 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6742 onMouseover : function(e, el)
6744 var cell = Roo.get(el);
6750 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6751 cell = cell.findParent('td', false, true);
6754 var row = cell.findParent('tr', false, true);
6755 var cellIndex = cell.dom.cellIndex;
6756 var rowIndex = row.dom.rowIndex - 1; // start from 0
6758 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6762 onMouseout : function(e, el)
6764 var cell = Roo.get(el);
6770 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6771 cell = cell.findParent('td', false, true);
6774 var row = cell.findParent('tr', false, true);
6775 var cellIndex = cell.dom.cellIndex;
6776 var rowIndex = row.dom.rowIndex - 1; // start from 0
6778 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6782 onClick : function(e, el)
6784 var cell = Roo.get(el);
6786 if(!cell || (!this.cellSelection && !this.rowSelection)){
6790 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6791 cell = cell.findParent('td', false, true);
6794 if(!cell || typeof(cell) == 'undefined'){
6798 var row = cell.findParent('tr', false, true);
6800 if(!row || typeof(row) == 'undefined'){
6804 var cellIndex = cell.dom.cellIndex;
6805 var rowIndex = this.getRowIndex(row);
6807 // why??? - should these not be based on SelectionModel?
6808 if(this.cellSelection){
6809 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6812 if(this.rowSelection){
6813 this.fireEvent('rowclick', this, row, rowIndex, e);
6819 onDblClick : function(e,el)
6821 var cell = Roo.get(el);
6823 if(!cell || (!this.cellSelection && !this.rowSelection)){
6827 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6828 cell = cell.findParent('td', false, true);
6831 if(!cell || typeof(cell) == 'undefined'){
6835 var row = cell.findParent('tr', false, true);
6837 if(!row || typeof(row) == 'undefined'){
6841 var cellIndex = cell.dom.cellIndex;
6842 var rowIndex = this.getRowIndex(row);
6844 if(this.cellSelection){
6845 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6848 if(this.rowSelection){
6849 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6853 sort : function(e,el)
6855 var col = Roo.get(el);
6857 if(!col.hasClass('sortable')){
6861 var sort = col.attr('sort');
6864 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6868 this.store.sortInfo = {field : sort, direction : dir};
6871 Roo.log("calling footer first");
6872 this.footer.onClick('first');
6875 this.store.load({ params : { start : 0 } });
6879 renderHeader : function()
6887 this.totalWidth = 0;
6889 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6891 var config = cm.config[i];
6895 cls : 'x-hcol-' + i,
6897 html: cm.getColumnHeader(i)
6902 if(typeof(config.sortable) != 'undefined' && config.sortable){
6904 c.html = '<i class="glyphicon"></i>' + c.html;
6907 // could use BS4 hidden-..-down
6909 if(typeof(config.lgHeader) != 'undefined'){
6910 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
6913 if(typeof(config.mdHeader) != 'undefined'){
6914 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6917 if(typeof(config.smHeader) != 'undefined'){
6918 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6921 if(typeof(config.xsHeader) != 'undefined'){
6922 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6929 if(typeof(config.tooltip) != 'undefined'){
6930 c.tooltip = config.tooltip;
6933 if(typeof(config.colspan) != 'undefined'){
6934 c.colspan = config.colspan;
6937 if(typeof(config.hidden) != 'undefined' && config.hidden){
6938 c.style += ' display:none;';
6941 if(typeof(config.dataIndex) != 'undefined'){
6942 c.sort = config.dataIndex;
6947 if(typeof(config.align) != 'undefined' && config.align.length){
6948 c.style += ' text-align:' + config.align + ';';
6951 if(typeof(config.width) != 'undefined'){
6952 c.style += ' width:' + config.width + 'px;';
6953 this.totalWidth += config.width;
6955 this.totalWidth += 100; // assume minimum of 100 per column?
6958 if(typeof(config.cls) != 'undefined'){
6959 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6962 ['xs','sm','md','lg'].map(function(size){
6964 if(typeof(config[size]) == 'undefined'){
6968 if (!config[size]) { // 0 = hidden
6969 // BS 4 '0' is treated as hide that column and below.
6970 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
6974 c.cls += ' col-' + size + '-' + config[size] + (
6975 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
6987 renderBody : function()
6997 colspan : this.cm.getColumnCount()
7007 renderFooter : function()
7017 colspan : this.cm.getColumnCount()
7031 // Roo.log('ds onload');
7036 var ds = this.store;
7038 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7039 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
7040 if (_this.store.sortInfo) {
7042 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
7043 e.select('i', true).addClass(['glyphicon-arrow-up']);
7046 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
7047 e.select('i', true).addClass(['glyphicon-arrow-down']);
7052 var tbody = this.mainBody;
7054 if(ds.getCount() > 0){
7055 ds.data.each(function(d,rowIndex){
7056 var row = this.renderRow(cm, ds, rowIndex);
7058 tbody.createChild(row);
7062 if(row.cellObjects.length){
7063 Roo.each(row.cellObjects, function(r){
7064 _this.renderCellObject(r);
7071 var tfoot = this.el.select('tfoot', true).first();
7073 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7075 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7077 var total = this.ds.getTotalCount();
7079 if(this.footer.pageSize < total){
7080 this.mainFoot.show();
7084 Roo.each(this.el.select('tbody td', true).elements, function(e){
7085 e.on('mouseover', _this.onMouseover, _this);
7088 Roo.each(this.el.select('tbody td', true).elements, function(e){
7089 e.on('mouseout', _this.onMouseout, _this);
7091 this.fireEvent('rowsrendered', this);
7097 onUpdate : function(ds,record)
7099 this.refreshRow(record);
7103 onRemove : function(ds, record, index, isUpdate){
7104 if(isUpdate !== true){
7105 this.fireEvent("beforerowremoved", this, index, record);
7107 var bt = this.mainBody.dom;
7109 var rows = this.el.select('tbody > tr', true).elements;
7111 if(typeof(rows[index]) != 'undefined'){
7112 bt.removeChild(rows[index].dom);
7115 // if(bt.rows[index]){
7116 // bt.removeChild(bt.rows[index]);
7119 if(isUpdate !== true){
7120 //this.stripeRows(index);
7121 //this.syncRowHeights(index, index);
7123 this.fireEvent("rowremoved", this, index, record);
7127 onAdd : function(ds, records, rowIndex)
7129 //Roo.log('on Add called');
7130 // - note this does not handle multiple adding very well..
7131 var bt = this.mainBody.dom;
7132 for (var i =0 ; i < records.length;i++) {
7133 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7134 //Roo.log(records[i]);
7135 //Roo.log(this.store.getAt(rowIndex+i));
7136 this.insertRow(this.store, rowIndex + i, false);
7143 refreshRow : function(record){
7144 var ds = this.store, index;
7145 if(typeof record == 'number'){
7147 record = ds.getAt(index);
7149 index = ds.indexOf(record);
7151 this.insertRow(ds, index, true);
7153 this.onRemove(ds, record, index+1, true);
7155 //this.syncRowHeights(index, index);
7157 this.fireEvent("rowupdated", this, index, record);
7160 insertRow : function(dm, rowIndex, isUpdate){
7163 this.fireEvent("beforerowsinserted", this, rowIndex);
7165 //var s = this.getScrollState();
7166 var row = this.renderRow(this.cm, this.store, rowIndex);
7167 // insert before rowIndex..
7168 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7172 if(row.cellObjects.length){
7173 Roo.each(row.cellObjects, function(r){
7174 _this.renderCellObject(r);
7179 this.fireEvent("rowsinserted", this, rowIndex);
7180 //this.syncRowHeights(firstRow, lastRow);
7181 //this.stripeRows(firstRow);
7188 getRowDom : function(rowIndex)
7190 var rows = this.el.select('tbody > tr', true).elements;
7192 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7195 // returns the object tree for a tr..
7198 renderRow : function(cm, ds, rowIndex)
7200 var d = ds.getAt(rowIndex);
7204 cls : 'x-row-' + rowIndex,
7208 var cellObjects = [];
7210 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7211 var config = cm.config[i];
7213 var renderer = cm.getRenderer(i);
7217 if(typeof(renderer) !== 'undefined'){
7218 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7220 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7221 // and are rendered into the cells after the row is rendered - using the id for the element.
7223 if(typeof(value) === 'object'){
7233 rowIndex : rowIndex,
7238 this.fireEvent('rowclass', this, rowcfg);
7242 cls : rowcfg.rowClass + ' x-col-' + i,
7244 html: (typeof(value) === 'object') ? '' : value
7251 if(typeof(config.colspan) != 'undefined'){
7252 td.colspan = config.colspan;
7255 if(typeof(config.hidden) != 'undefined' && config.hidden){
7256 td.style += ' display:none;';
7259 if(typeof(config.align) != 'undefined' && config.align.length){
7260 td.style += ' text-align:' + config.align + ';';
7262 if(typeof(config.valign) != 'undefined' && config.valign.length){
7263 td.style += ' vertical-align:' + config.valign + ';';
7266 if(typeof(config.width) != 'undefined'){
7267 td.style += ' width:' + config.width + 'px;';
7270 if(typeof(config.cursor) != 'undefined'){
7271 td.style += ' cursor:' + config.cursor + ';';
7274 if(typeof(config.cls) != 'undefined'){
7275 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7278 ['xs','sm','md','lg'].map(function(size){
7280 if(typeof(config[size]) == 'undefined'){
7286 if (!config[size]) { // 0 = hidden
7287 // BS 4 '0' is treated as hide that column and below.
7288 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7292 td.cls += ' col-' + size + '-' + config[size] + (
7293 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7303 row.cellObjects = cellObjects;
7311 onBeforeLoad : function()
7320 this.el.select('tbody', true).first().dom.innerHTML = '';
7323 * Show or hide a row.
7324 * @param {Number} rowIndex to show or hide
7325 * @param {Boolean} state hide
7327 setRowVisibility : function(rowIndex, state)
7329 var bt = this.mainBody.dom;
7331 var rows = this.el.select('tbody > tr', true).elements;
7333 if(typeof(rows[rowIndex]) == 'undefined'){
7336 rows[rowIndex].dom.style.display = state ? '' : 'none';
7340 getSelectionModel : function(){
7342 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7344 return this.selModel;
7347 * Render the Roo.bootstrap object from renderder
7349 renderCellObject : function(r)
7353 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7355 var t = r.cfg.render(r.container);
7358 Roo.each(r.cfg.cn, function(c){
7360 container: t.getChildContainer(),
7363 _this.renderCellObject(child);
7368 getRowIndex : function(row)
7372 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7383 * Returns the grid's underlying element = used by panel.Grid
7384 * @return {Element} The element
7386 getGridEl : function(){
7390 * Forces a resize - used by panel.Grid
7391 * @return {Element} The element
7393 autoSize : function()
7395 //var ctr = Roo.get(this.container.dom.parentElement);
7396 var ctr = Roo.get(this.el.dom);
7398 var thd = this.getGridEl().select('thead',true).first();
7399 var tbd = this.getGridEl().select('tbody', true).first();
7400 var tfd = this.getGridEl().select('tfoot', true).first();
7402 var cw = ctr.getWidth();
7406 tbd.setSize(ctr.getWidth(),
7407 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7409 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7412 cw = Math.max(cw, this.totalWidth);
7413 this.getGridEl().select('tr',true).setWidth(cw);
7414 // resize 'expandable coloumn?
7416 return; // we doe not have a view in this design..
7419 onBodyScroll: function()
7421 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7423 this.mainHead.setStyle({
7424 'position' : 'relative',
7425 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7431 var scrollHeight = this.mainBody.dom.scrollHeight;
7433 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7435 var height = this.mainBody.getHeight();
7437 if(scrollHeight - height == scrollTop) {
7439 var total = this.ds.getTotalCount();
7441 if(this.footer.cursor + this.footer.pageSize < total){
7443 this.footer.ds.load({
7445 start : this.footer.cursor + this.footer.pageSize,
7446 limit : this.footer.pageSize
7456 onHeaderChange : function()
7458 var header = this.renderHeader();
7459 var table = this.el.select('table', true).first();
7461 this.mainHead.remove();
7462 this.mainHead = table.createChild(header, this.mainBody, false);
7465 onHiddenChange : function(colModel, colIndex, hidden)
7467 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7468 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7470 this.CSS.updateRule(thSelector, "display", "");
7471 this.CSS.updateRule(tdSelector, "display", "");
7474 this.CSS.updateRule(thSelector, "display", "none");
7475 this.CSS.updateRule(tdSelector, "display", "none");
7478 this.onHeaderChange();
7482 setColumnWidth: function(col_index, width)
7484 // width = "md-2 xs-2..."
7485 if(!this.colModel.config[col_index]) {
7489 var w = width.split(" ");
7491 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7493 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7496 for(var j = 0; j < w.length; j++) {
7502 var size_cls = w[j].split("-");
7504 if(!Number.isInteger(size_cls[1] * 1)) {
7508 if(!this.colModel.config[col_index][size_cls[0]]) {
7512 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7516 h_row[0].classList.replace(
7517 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7518 "col-"+size_cls[0]+"-"+size_cls[1]
7521 for(var i = 0; i < rows.length; i++) {
7523 var size_cls = w[j].split("-");
7525 if(!Number.isInteger(size_cls[1] * 1)) {
7529 if(!this.colModel.config[col_index][size_cls[0]]) {
7533 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7537 rows[i].classList.replace(
7538 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7539 "col-"+size_cls[0]+"-"+size_cls[1]
7543 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7558 * @class Roo.bootstrap.TableCell
7559 * @extends Roo.bootstrap.Component
7560 * Bootstrap TableCell class
7561 * @cfg {String} html cell contain text
7562 * @cfg {String} cls cell class
7563 * @cfg {String} tag cell tag (td|th) default td
7564 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7565 * @cfg {String} align Aligns the content in a cell
7566 * @cfg {String} axis Categorizes cells
7567 * @cfg {String} bgcolor Specifies the background color of a cell
7568 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7569 * @cfg {Number} colspan Specifies the number of columns a cell should span
7570 * @cfg {String} headers Specifies one or more header cells a cell is related to
7571 * @cfg {Number} height Sets the height of a cell
7572 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7573 * @cfg {Number} rowspan Sets the number of rows a cell should span
7574 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7575 * @cfg {String} valign Vertical aligns the content in a cell
7576 * @cfg {Number} width Specifies the width of a cell
7579 * Create a new TableCell
7580 * @param {Object} config The config object
7583 Roo.bootstrap.TableCell = function(config){
7584 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7587 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7607 getAutoCreate : function(){
7608 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7628 cfg.align=this.align
7634 cfg.bgcolor=this.bgcolor
7637 cfg.charoff=this.charoff
7640 cfg.colspan=this.colspan
7643 cfg.headers=this.headers
7646 cfg.height=this.height
7649 cfg.nowrap=this.nowrap
7652 cfg.rowspan=this.rowspan
7655 cfg.scope=this.scope
7658 cfg.valign=this.valign
7661 cfg.width=this.width
7680 * @class Roo.bootstrap.TableRow
7681 * @extends Roo.bootstrap.Component
7682 * Bootstrap TableRow class
7683 * @cfg {String} cls row class
7684 * @cfg {String} align Aligns the content in a table row
7685 * @cfg {String} bgcolor Specifies a background color for a table row
7686 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7687 * @cfg {String} valign Vertical aligns the content in a table row
7690 * Create a new TableRow
7691 * @param {Object} config The config object
7694 Roo.bootstrap.TableRow = function(config){
7695 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7698 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7706 getAutoCreate : function(){
7707 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7717 cfg.align = this.align;
7720 cfg.bgcolor = this.bgcolor;
7723 cfg.charoff = this.charoff;
7726 cfg.valign = this.valign;
7744 * @class Roo.bootstrap.TableBody
7745 * @extends Roo.bootstrap.Component
7746 * Bootstrap TableBody class
7747 * @cfg {String} cls element class
7748 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7749 * @cfg {String} align Aligns the content inside the element
7750 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7751 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7754 * Create a new TableBody
7755 * @param {Object} config The config object
7758 Roo.bootstrap.TableBody = function(config){
7759 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7762 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7770 getAutoCreate : function(){
7771 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7785 cfg.align = this.align;
7788 cfg.charoff = this.charoff;
7791 cfg.valign = this.valign;
7798 // initEvents : function()
7805 // this.store = Roo.factory(this.store, Roo.data);
7806 // this.store.on('load', this.onLoad, this);
7808 // this.store.load();
7812 // onLoad: function ()
7814 // this.fireEvent('load', this);
7824 * Ext JS Library 1.1.1
7825 * Copyright(c) 2006-2007, Ext JS, LLC.
7827 * Originally Released Under LGPL - original licence link has changed is not relivant.
7830 * <script type="text/javascript">
7833 // as we use this in bootstrap.
7834 Roo.namespace('Roo.form');
7836 * @class Roo.form.Action
7837 * Internal Class used to handle form actions
7839 * @param {Roo.form.BasicForm} el The form element or its id
7840 * @param {Object} config Configuration options
7845 // define the action interface
7846 Roo.form.Action = function(form, options){
7848 this.options = options || {};
7851 * Client Validation Failed
7854 Roo.form.Action.CLIENT_INVALID = 'client';
7856 * Server Validation Failed
7859 Roo.form.Action.SERVER_INVALID = 'server';
7861 * Connect to Server Failed
7864 Roo.form.Action.CONNECT_FAILURE = 'connect';
7866 * Reading Data from Server Failed
7869 Roo.form.Action.LOAD_FAILURE = 'load';
7871 Roo.form.Action.prototype = {
7873 failureType : undefined,
7874 response : undefined,
7878 run : function(options){
7883 success : function(response){
7888 handleResponse : function(response){
7892 // default connection failure
7893 failure : function(response){
7895 this.response = response;
7896 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7897 this.form.afterAction(this, false);
7900 processResponse : function(response){
7901 this.response = response;
7902 if(!response.responseText){
7905 this.result = this.handleResponse(response);
7909 // utility functions used internally
7910 getUrl : function(appendParams){
7911 var url = this.options.url || this.form.url || this.form.el.dom.action;
7913 var p = this.getParams();
7915 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7921 getMethod : function(){
7922 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7925 getParams : function(){
7926 var bp = this.form.baseParams;
7927 var p = this.options.params;
7929 if(typeof p == "object"){
7930 p = Roo.urlEncode(Roo.applyIf(p, bp));
7931 }else if(typeof p == 'string' && bp){
7932 p += '&' + Roo.urlEncode(bp);
7935 p = Roo.urlEncode(bp);
7940 createCallback : function(){
7942 success: this.success,
7943 failure: this.failure,
7945 timeout: (this.form.timeout*1000),
7946 upload: this.form.fileUpload ? this.success : undefined
7951 Roo.form.Action.Submit = function(form, options){
7952 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7955 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7958 haveProgress : false,
7959 uploadComplete : false,
7961 // uploadProgress indicator.
7962 uploadProgress : function()
7964 if (!this.form.progressUrl) {
7968 if (!this.haveProgress) {
7969 Roo.MessageBox.progress("Uploading", "Uploading");
7971 if (this.uploadComplete) {
7972 Roo.MessageBox.hide();
7976 this.haveProgress = true;
7978 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7980 var c = new Roo.data.Connection();
7982 url : this.form.progressUrl,
7987 success : function(req){
7988 //console.log(data);
7992 rdata = Roo.decode(req.responseText)
7994 Roo.log("Invalid data from server..");
7998 if (!rdata || !rdata.success) {
8000 Roo.MessageBox.alert(Roo.encode(rdata));
8003 var data = rdata.data;
8005 if (this.uploadComplete) {
8006 Roo.MessageBox.hide();
8011 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
8012 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
8015 this.uploadProgress.defer(2000,this);
8018 failure: function(data) {
8019 Roo.log('progress url failed ');
8030 // run get Values on the form, so it syncs any secondary forms.
8031 this.form.getValues();
8033 var o = this.options;
8034 var method = this.getMethod();
8035 var isPost = method == 'POST';
8036 if(o.clientValidation === false || this.form.isValid()){
8038 if (this.form.progressUrl) {
8039 this.form.findField('UPLOAD_IDENTIFIER').setValue(
8040 (new Date() * 1) + '' + Math.random());
8045 Roo.Ajax.request(Roo.apply(this.createCallback(), {
8046 form:this.form.el.dom,
8047 url:this.getUrl(!isPost),
8049 params:isPost ? this.getParams() : null,
8050 isUpload: this.form.fileUpload,
8051 formData : this.form.formData
8054 this.uploadProgress();
8056 }else if (o.clientValidation !== false){ // client validation failed
8057 this.failureType = Roo.form.Action.CLIENT_INVALID;
8058 this.form.afterAction(this, false);
8062 success : function(response)
8064 this.uploadComplete= true;
8065 if (this.haveProgress) {
8066 Roo.MessageBox.hide();
8070 var result = this.processResponse(response);
8071 if(result === true || result.success){
8072 this.form.afterAction(this, true);
8076 this.form.markInvalid(result.errors);
8077 this.failureType = Roo.form.Action.SERVER_INVALID;
8079 this.form.afterAction(this, false);
8081 failure : function(response)
8083 this.uploadComplete= true;
8084 if (this.haveProgress) {
8085 Roo.MessageBox.hide();
8088 this.response = response;
8089 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8090 this.form.afterAction(this, false);
8093 handleResponse : function(response){
8094 if(this.form.errorReader){
8095 var rs = this.form.errorReader.read(response);
8098 for(var i = 0, len = rs.records.length; i < len; i++) {
8099 var r = rs.records[i];
8103 if(errors.length < 1){
8107 success : rs.success,
8113 ret = Roo.decode(response.responseText);
8117 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8127 Roo.form.Action.Load = function(form, options){
8128 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8129 this.reader = this.form.reader;
8132 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8137 Roo.Ajax.request(Roo.apply(
8138 this.createCallback(), {
8139 method:this.getMethod(),
8140 url:this.getUrl(false),
8141 params:this.getParams()
8145 success : function(response){
8147 var result = this.processResponse(response);
8148 if(result === true || !result.success || !result.data){
8149 this.failureType = Roo.form.Action.LOAD_FAILURE;
8150 this.form.afterAction(this, false);
8153 this.form.clearInvalid();
8154 this.form.setValues(result.data);
8155 this.form.afterAction(this, true);
8158 handleResponse : function(response){
8159 if(this.form.reader){
8160 var rs = this.form.reader.read(response);
8161 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8163 success : rs.success,
8167 return Roo.decode(response.responseText);
8171 Roo.form.Action.ACTION_TYPES = {
8172 'load' : Roo.form.Action.Load,
8173 'submit' : Roo.form.Action.Submit
8182 * @class Roo.bootstrap.Form
8183 * @extends Roo.bootstrap.Component
8184 * Bootstrap Form class
8185 * @cfg {String} method GET | POST (default POST)
8186 * @cfg {String} labelAlign top | left (default top)
8187 * @cfg {String} align left | right - for navbars
8188 * @cfg {Boolean} loadMask load mask when submit (default true)
8193 * @param {Object} config The config object
8197 Roo.bootstrap.Form = function(config){
8199 Roo.bootstrap.Form.superclass.constructor.call(this, config);
8201 Roo.bootstrap.Form.popover.apply();
8205 * @event clientvalidation
8206 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8207 * @param {Form} this
8208 * @param {Boolean} valid true if the form has passed client-side validation
8210 clientvalidation: true,
8212 * @event beforeaction
8213 * Fires before any action is performed. Return false to cancel the action.
8214 * @param {Form} this
8215 * @param {Action} action The action to be performed
8219 * @event actionfailed
8220 * Fires when an action fails.
8221 * @param {Form} this
8222 * @param {Action} action The action that failed
8224 actionfailed : true,
8226 * @event actioncomplete
8227 * Fires when an action is completed.
8228 * @param {Form} this
8229 * @param {Action} action The action that completed
8231 actioncomplete : true
8235 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
8238 * @cfg {String} method
8239 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8244 * The URL to use for form actions if one isn't supplied in the action options.
8247 * @cfg {Boolean} fileUpload
8248 * Set to true if this form is a file upload.
8252 * @cfg {Object} baseParams
8253 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8257 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8261 * @cfg {Sting} align (left|right) for navbar forms
8266 activeAction : null,
8269 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8270 * element by passing it or its id or mask the form itself by passing in true.
8273 waitMsgTarget : false,
8278 * @cfg {Boolean} errorMask (true|false) default false
8283 * @cfg {Number} maskOffset Default 100
8288 * @cfg {Boolean} maskBody
8292 getAutoCreate : function(){
8296 method : this.method || 'POST',
8297 id : this.id || Roo.id(),
8300 if (this.parent().xtype.match(/^Nav/)) {
8301 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8305 if (this.labelAlign == 'left' ) {
8306 cfg.cls += ' form-horizontal';
8312 initEvents : function()
8314 this.el.on('submit', this.onSubmit, this);
8315 // this was added as random key presses on the form where triggering form submit.
8316 this.el.on('keypress', function(e) {
8317 if (e.getCharCode() != 13) {
8320 // we might need to allow it for textareas.. and some other items.
8321 // check e.getTarget().
8323 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8327 Roo.log("keypress blocked");
8335 onSubmit : function(e){
8340 * Returns true if client-side validation on the form is successful.
8343 isValid : function(){
8344 var items = this.getItems();
8348 items.each(function(f){
8354 Roo.log('invalid field: ' + f.name);
8358 if(!target && f.el.isVisible(true)){
8364 if(this.errorMask && !valid){
8365 Roo.bootstrap.Form.popover.mask(this, target);
8372 * Returns true if any fields in this form have changed since their original load.
8375 isDirty : function(){
8377 var items = this.getItems();
8378 items.each(function(f){
8388 * Performs a predefined action (submit or load) or custom actions you define on this form.
8389 * @param {String} actionName The name of the action type
8390 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8391 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8392 * accept other config options):
8394 Property Type Description
8395 ---------------- --------------- ----------------------------------------------------------------------------------
8396 url String The url for the action (defaults to the form's url)
8397 method String The form method to use (defaults to the form's method, or POST if not defined)
8398 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8399 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8400 validate the form on the client (defaults to false)
8402 * @return {BasicForm} this
8404 doAction : function(action, options){
8405 if(typeof action == 'string'){
8406 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8408 if(this.fireEvent('beforeaction', this, action) !== false){
8409 this.beforeAction(action);
8410 action.run.defer(100, action);
8416 beforeAction : function(action){
8417 var o = action.options;
8422 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8424 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8427 // not really supported yet.. ??
8429 //if(this.waitMsgTarget === true){
8430 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8431 //}else if(this.waitMsgTarget){
8432 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8433 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8435 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8441 afterAction : function(action, success){
8442 this.activeAction = null;
8443 var o = action.options;
8448 Roo.get(document.body).unmask();
8454 //if(this.waitMsgTarget === true){
8455 // this.el.unmask();
8456 //}else if(this.waitMsgTarget){
8457 // this.waitMsgTarget.unmask();
8459 // Roo.MessageBox.updateProgress(1);
8460 // Roo.MessageBox.hide();
8467 Roo.callback(o.success, o.scope, [this, action]);
8468 this.fireEvent('actioncomplete', this, action);
8472 // failure condition..
8473 // we have a scenario where updates need confirming.
8474 // eg. if a locking scenario exists..
8475 // we look for { errors : { needs_confirm : true }} in the response.
8477 (typeof(action.result) != 'undefined') &&
8478 (typeof(action.result.errors) != 'undefined') &&
8479 (typeof(action.result.errors.needs_confirm) != 'undefined')
8482 Roo.log("not supported yet");
8485 Roo.MessageBox.confirm(
8486 "Change requires confirmation",
8487 action.result.errorMsg,
8492 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8502 Roo.callback(o.failure, o.scope, [this, action]);
8503 // show an error message if no failed handler is set..
8504 if (!this.hasListener('actionfailed')) {
8505 Roo.log("need to add dialog support");
8507 Roo.MessageBox.alert("Error",
8508 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8509 action.result.errorMsg :
8510 "Saving Failed, please check your entries or try again"
8515 this.fireEvent('actionfailed', this, action);
8520 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8521 * @param {String} id The value to search for
8524 findField : function(id){
8525 var items = this.getItems();
8526 var field = items.get(id);
8528 items.each(function(f){
8529 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8536 return field || null;
8539 * Mark fields in this form invalid in bulk.
8540 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8541 * @return {BasicForm} this
8543 markInvalid : function(errors){
8544 if(errors instanceof Array){
8545 for(var i = 0, len = errors.length; i < len; i++){
8546 var fieldError = errors[i];
8547 var f = this.findField(fieldError.id);
8549 f.markInvalid(fieldError.msg);
8555 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8556 field.markInvalid(errors[id]);
8560 //Roo.each(this.childForms || [], function (f) {
8561 // f.markInvalid(errors);
8568 * Set values for fields in this form in bulk.
8569 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8570 * @return {BasicForm} this
8572 setValues : function(values){
8573 if(values instanceof Array){ // array of objects
8574 for(var i = 0, len = values.length; i < len; i++){
8576 var f = this.findField(v.id);
8578 f.setValue(v.value);
8579 if(this.trackResetOnLoad){
8580 f.originalValue = f.getValue();
8584 }else{ // object hash
8587 if(typeof values[id] != 'function' && (field = this.findField(id))){
8589 if (field.setFromData &&
8591 field.displayField &&
8592 // combos' with local stores can
8593 // be queried via setValue()
8594 // to set their value..
8595 (field.store && !field.store.isLocal)
8599 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8600 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8601 field.setFromData(sd);
8603 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8605 field.setFromData(values);
8608 field.setValue(values[id]);
8612 if(this.trackResetOnLoad){
8613 field.originalValue = field.getValue();
8619 //Roo.each(this.childForms || [], function (f) {
8620 // f.setValues(values);
8627 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8628 * they are returned as an array.
8629 * @param {Boolean} asString
8632 getValues : function(asString){
8633 //if (this.childForms) {
8634 // copy values from the child forms
8635 // Roo.each(this.childForms, function (f) {
8636 // this.setValues(f.getValues());
8642 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8643 if(asString === true){
8646 return Roo.urlDecode(fs);
8650 * Returns the fields in this form as an object with key/value pairs.
8651 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8654 getFieldValues : function(with_hidden)
8656 var items = this.getItems();
8658 items.each(function(f){
8664 var v = f.getValue();
8666 if (f.inputType =='radio') {
8667 if (typeof(ret[f.getName()]) == 'undefined') {
8668 ret[f.getName()] = ''; // empty..
8671 if (!f.el.dom.checked) {
8679 if(f.xtype == 'MoneyField'){
8680 ret[f.currencyName] = f.getCurrency();
8683 // not sure if this supported any more..
8684 if ((typeof(v) == 'object') && f.getRawValue) {
8685 v = f.getRawValue() ; // dates..
8687 // combo boxes where name != hiddenName...
8688 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8689 ret[f.name] = f.getRawValue();
8691 ret[f.getName()] = v;
8698 * Clears all invalid messages in this form.
8699 * @return {BasicForm} this
8701 clearInvalid : function(){
8702 var items = this.getItems();
8704 items.each(function(f){
8713 * @return {BasicForm} this
8716 var items = this.getItems();
8717 items.each(function(f){
8721 Roo.each(this.childForms || [], function (f) {
8729 getItems : function()
8731 var r=new Roo.util.MixedCollection(false, function(o){
8732 return o.id || (o.id = Roo.id());
8734 var iter = function(el) {
8741 Roo.each(el.items,function(e) {
8750 hideFields : function(items)
8752 Roo.each(items, function(i){
8754 var f = this.findField(i);
8765 showFields : function(items)
8767 Roo.each(items, function(i){
8769 var f = this.findField(i);
8782 Roo.apply(Roo.bootstrap.Form, {
8809 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8810 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8811 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8812 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8815 this.maskEl.top.enableDisplayMode("block");
8816 this.maskEl.left.enableDisplayMode("block");
8817 this.maskEl.bottom.enableDisplayMode("block");
8818 this.maskEl.right.enableDisplayMode("block");
8820 this.toolTip = new Roo.bootstrap.Tooltip({
8821 cls : 'roo-form-error-popover',
8823 'left' : ['r-l', [-2,0], 'right'],
8824 'right' : ['l-r', [2,0], 'left'],
8825 'bottom' : ['tl-bl', [0,2], 'top'],
8826 'top' : [ 'bl-tl', [0,-2], 'bottom']
8830 this.toolTip.render(Roo.get(document.body));
8832 this.toolTip.el.enableDisplayMode("block");
8834 Roo.get(document.body).on('click', function(){
8838 Roo.get(document.body).on('touchstart', function(){
8842 this.isApplied = true
8845 mask : function(form, target)
8849 this.target = target;
8851 if(!this.form.errorMask || !target.el){
8855 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8857 Roo.log(scrollable);
8859 var ot = this.target.el.calcOffsetsTo(scrollable);
8861 var scrollTo = ot[1] - this.form.maskOffset;
8863 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8865 scrollable.scrollTo('top', scrollTo);
8867 var box = this.target.el.getBox();
8869 var zIndex = Roo.bootstrap.Modal.zIndex++;
8872 this.maskEl.top.setStyle('position', 'absolute');
8873 this.maskEl.top.setStyle('z-index', zIndex);
8874 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8875 this.maskEl.top.setLeft(0);
8876 this.maskEl.top.setTop(0);
8877 this.maskEl.top.show();
8879 this.maskEl.left.setStyle('position', 'absolute');
8880 this.maskEl.left.setStyle('z-index', zIndex);
8881 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8882 this.maskEl.left.setLeft(0);
8883 this.maskEl.left.setTop(box.y - this.padding);
8884 this.maskEl.left.show();
8886 this.maskEl.bottom.setStyle('position', 'absolute');
8887 this.maskEl.bottom.setStyle('z-index', zIndex);
8888 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8889 this.maskEl.bottom.setLeft(0);
8890 this.maskEl.bottom.setTop(box.bottom + this.padding);
8891 this.maskEl.bottom.show();
8893 this.maskEl.right.setStyle('position', 'absolute');
8894 this.maskEl.right.setStyle('z-index', zIndex);
8895 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8896 this.maskEl.right.setLeft(box.right + this.padding);
8897 this.maskEl.right.setTop(box.y - this.padding);
8898 this.maskEl.right.show();
8900 this.toolTip.bindEl = this.target.el;
8902 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8904 var tip = this.target.blankText;
8906 if(this.target.getValue() !== '' ) {
8908 if (this.target.invalidText.length) {
8909 tip = this.target.invalidText;
8910 } else if (this.target.regexText.length){
8911 tip = this.target.regexText;
8915 this.toolTip.show(tip);
8917 this.intervalID = window.setInterval(function() {
8918 Roo.bootstrap.Form.popover.unmask();
8921 window.onwheel = function(){ return false;};
8923 (function(){ this.isMasked = true; }).defer(500, this);
8929 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8933 this.maskEl.top.setStyle('position', 'absolute');
8934 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8935 this.maskEl.top.hide();
8937 this.maskEl.left.setStyle('position', 'absolute');
8938 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8939 this.maskEl.left.hide();
8941 this.maskEl.bottom.setStyle('position', 'absolute');
8942 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8943 this.maskEl.bottom.hide();
8945 this.maskEl.right.setStyle('position', 'absolute');
8946 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8947 this.maskEl.right.hide();
8949 this.toolTip.hide();
8951 this.toolTip.el.hide();
8953 window.onwheel = function(){ return true;};
8955 if(this.intervalID){
8956 window.clearInterval(this.intervalID);
8957 this.intervalID = false;
8960 this.isMasked = false;
8970 * Ext JS Library 1.1.1
8971 * Copyright(c) 2006-2007, Ext JS, LLC.
8973 * Originally Released Under LGPL - original licence link has changed is not relivant.
8976 * <script type="text/javascript">
8979 * @class Roo.form.VTypes
8980 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8983 Roo.form.VTypes = function(){
8984 // closure these in so they are only created once.
8985 var alpha = /^[a-zA-Z_]+$/;
8986 var alphanum = /^[a-zA-Z0-9_]+$/;
8987 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8988 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8990 // All these messages and functions are configurable
8993 * The function used to validate email addresses
8994 * @param {String} value The email address
8996 'email' : function(v){
8997 return email.test(v);
9000 * The error text to display when the email validation function returns false
9003 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
9005 * The keystroke filter mask to be applied on email input
9008 'emailMask' : /[a-z0-9_\.\-@]/i,
9011 * The function used to validate URLs
9012 * @param {String} value The URL
9014 'url' : function(v){
9018 * The error text to display when the url validation function returns false
9021 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
9024 * The function used to validate alpha values
9025 * @param {String} value The value
9027 'alpha' : function(v){
9028 return alpha.test(v);
9031 * The error text to display when the alpha validation function returns false
9034 'alphaText' : 'This field should only contain letters and _',
9036 * The keystroke filter mask to be applied on alpha input
9039 'alphaMask' : /[a-z_]/i,
9042 * The function used to validate alphanumeric values
9043 * @param {String} value The value
9045 'alphanum' : function(v){
9046 return alphanum.test(v);
9049 * The error text to display when the alphanumeric validation function returns false
9052 'alphanumText' : 'This field should only contain letters, numbers and _',
9054 * The keystroke filter mask to be applied on alphanumeric input
9057 'alphanumMask' : /[a-z0-9_]/i
9067 * @class Roo.bootstrap.Input
9068 * @extends Roo.bootstrap.Component
9069 * Bootstrap Input class
9070 * @cfg {Boolean} disabled is it disabled
9071 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
9072 * @cfg {String} name name of the input
9073 * @cfg {string} fieldLabel - the label associated
9074 * @cfg {string} placeholder - placeholder to put in text.
9075 * @cfg {string} before - input group add on before
9076 * @cfg {string} after - input group add on after
9077 * @cfg {string} size - (lg|sm) or leave empty..
9078 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9079 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9080 * @cfg {Number} md colspan out of 12 for computer-sized screens
9081 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9082 * @cfg {string} value default value of the input
9083 * @cfg {Number} labelWidth set the width of label
9084 * @cfg {Number} labellg set the width of label (1-12)
9085 * @cfg {Number} labelmd set the width of label (1-12)
9086 * @cfg {Number} labelsm set the width of label (1-12)
9087 * @cfg {Number} labelxs set the width of label (1-12)
9088 * @cfg {String} labelAlign (top|left)
9089 * @cfg {Boolean} readOnly Specifies that the field should be read-only
9090 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9091 * @cfg {String} indicatorpos (left|right) default left
9092 * @cfg {String} capture (user|camera) use for file input only. (default empty)
9093 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9095 * @cfg {String} align (left|center|right) Default left
9096 * @cfg {Boolean} forceFeedback (true|false) Default false
9099 * Create a new Input
9100 * @param {Object} config The config object
9103 Roo.bootstrap.Input = function(config){
9105 Roo.bootstrap.Input.superclass.constructor.call(this, config);
9110 * Fires when this field receives input focus.
9111 * @param {Roo.form.Field} this
9116 * Fires when this field loses input focus.
9117 * @param {Roo.form.Field} this
9122 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
9123 * {@link Roo.EventObject#getKey} to determine which key was pressed.
9124 * @param {Roo.form.Field} this
9125 * @param {Roo.EventObject} e The event object
9130 * Fires just before the field blurs if the field value has changed.
9131 * @param {Roo.form.Field} this
9132 * @param {Mixed} newValue The new value
9133 * @param {Mixed} oldValue The original value
9138 * Fires after the field has been marked as invalid.
9139 * @param {Roo.form.Field} this
9140 * @param {String} msg The validation message
9145 * Fires after the field has been validated with no errors.
9146 * @param {Roo.form.Field} this
9151 * Fires after the key up
9152 * @param {Roo.form.Field} this
9153 * @param {Roo.EventObject} e The event Object
9159 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
9161 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9162 automatic validation (defaults to "keyup").
9164 validationEvent : "keyup",
9166 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9168 validateOnBlur : true,
9170 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9172 validationDelay : 250,
9174 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9176 focusClass : "x-form-focus", // not needed???
9180 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9182 invalidClass : "has-warning",
9185 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9187 validClass : "has-success",
9190 * @cfg {Boolean} hasFeedback (true|false) default true
9195 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9197 invalidFeedbackClass : "glyphicon-warning-sign",
9200 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9202 validFeedbackClass : "glyphicon-ok",
9205 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9207 selectOnFocus : false,
9210 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9214 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9219 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9221 disableKeyFilter : false,
9224 * @cfg {Boolean} disabled True to disable the field (defaults to false).
9228 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9232 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9234 blankText : "Please complete this mandatory field",
9237 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9241 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9243 maxLength : Number.MAX_VALUE,
9245 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9247 minLengthText : "The minimum length for this field is {0}",
9249 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9251 maxLengthText : "The maximum length for this field is {0}",
9255 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9256 * If available, this function will be called only after the basic validators all return true, and will be passed the
9257 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9261 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9262 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9263 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9267 * @cfg {String} regexText -- Depricated - use Invalid Text
9272 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9278 autocomplete: false,
9297 formatedValue : false,
9298 forceFeedback : false,
9300 indicatorpos : 'left',
9310 parentLabelAlign : function()
9313 while (parent.parent()) {
9314 parent = parent.parent();
9315 if (typeof(parent.labelAlign) !='undefined') {
9316 return parent.labelAlign;
9323 getAutoCreate : function()
9325 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9331 if(this.inputType != 'hidden'){
9332 cfg.cls = 'form-group' //input-group
9338 type : this.inputType,
9340 cls : 'form-control',
9341 placeholder : this.placeholder || '',
9342 autocomplete : this.autocomplete || 'new-password'
9345 if(this.capture.length){
9346 input.capture = this.capture;
9349 if(this.accept.length){
9350 input.accept = this.accept + "/*";
9354 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9357 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9358 input.maxLength = this.maxLength;
9361 if (this.disabled) {
9362 input.disabled=true;
9365 if (this.readOnly) {
9366 input.readonly=true;
9370 input.name = this.name;
9374 input.cls += ' input-' + this.size;
9378 ['xs','sm','md','lg'].map(function(size){
9379 if (settings[size]) {
9380 cfg.cls += ' col-' + size + '-' + settings[size];
9384 var inputblock = input;
9388 cls: 'glyphicon form-control-feedback'
9391 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9394 cls : 'has-feedback',
9402 if (this.before || this.after) {
9405 cls : 'input-group',
9409 if (this.before && typeof(this.before) == 'string') {
9411 inputblock.cn.push({
9413 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9417 if (this.before && typeof(this.before) == 'object') {
9418 this.before = Roo.factory(this.before);
9420 inputblock.cn.push({
9422 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9423 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9427 inputblock.cn.push(input);
9429 if (this.after && typeof(this.after) == 'string') {
9430 inputblock.cn.push({
9432 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9436 if (this.after && typeof(this.after) == 'object') {
9437 this.after = Roo.factory(this.after);
9439 inputblock.cn.push({
9441 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9442 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9446 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9447 inputblock.cls += ' has-feedback';
9448 inputblock.cn.push(feedback);
9453 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9454 tooltip : 'This field is required'
9456 if (Roo.bootstrap.version == 4) {
9459 style : 'display-none'
9462 if (align ==='left' && this.fieldLabel.length) {
9464 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
9471 cls : 'control-label col-form-label',
9472 html : this.fieldLabel
9483 var labelCfg = cfg.cn[1];
9484 var contentCfg = cfg.cn[2];
9486 if(this.indicatorpos == 'right'){
9491 cls : 'control-label col-form-label',
9495 html : this.fieldLabel
9509 labelCfg = cfg.cn[0];
9510 contentCfg = cfg.cn[1];
9514 if(this.labelWidth > 12){
9515 labelCfg.style = "width: " + this.labelWidth + 'px';
9518 if(this.labelWidth < 13 && this.labelmd == 0){
9519 this.labelmd = this.labelWidth;
9522 if(this.labellg > 0){
9523 labelCfg.cls += ' col-lg-' + this.labellg;
9524 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9527 if(this.labelmd > 0){
9528 labelCfg.cls += ' col-md-' + this.labelmd;
9529 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9532 if(this.labelsm > 0){
9533 labelCfg.cls += ' col-sm-' + this.labelsm;
9534 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9537 if(this.labelxs > 0){
9538 labelCfg.cls += ' col-xs-' + this.labelxs;
9539 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9543 } else if ( this.fieldLabel.length) {
9548 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9549 tooltip : 'This field is required'
9553 //cls : 'input-group-addon',
9554 html : this.fieldLabel
9562 if(this.indicatorpos == 'right'){
9567 //cls : 'input-group-addon',
9568 html : this.fieldLabel
9573 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9574 tooltip : 'This field is required'
9594 if (this.parentType === 'Navbar' && this.parent().bar) {
9595 cfg.cls += ' navbar-form';
9598 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9599 // on BS4 we do this only if not form
9600 cfg.cls += ' navbar-form';
9608 * return the real input element.
9610 inputEl: function ()
9612 return this.el.select('input.form-control',true).first();
9615 tooltipEl : function()
9617 return this.inputEl();
9620 indicatorEl : function()
9622 if (Roo.bootstrap.version == 4) {
9623 return false; // not enabled in v4 yet.
9626 var indicator = this.el.select('i.roo-required-indicator',true).first();
9636 setDisabled : function(v)
9638 var i = this.inputEl().dom;
9640 i.removeAttribute('disabled');
9644 i.setAttribute('disabled','true');
9646 initEvents : function()
9649 this.inputEl().on("keydown" , this.fireKey, this);
9650 this.inputEl().on("focus", this.onFocus, this);
9651 this.inputEl().on("blur", this.onBlur, this);
9653 this.inputEl().relayEvent('keyup', this);
9655 this.indicator = this.indicatorEl();
9658 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9661 // reference to original value for reset
9662 this.originalValue = this.getValue();
9663 //Roo.form.TextField.superclass.initEvents.call(this);
9664 if(this.validationEvent == 'keyup'){
9665 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9666 this.inputEl().on('keyup', this.filterValidation, this);
9668 else if(this.validationEvent !== false){
9669 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9672 if(this.selectOnFocus){
9673 this.on("focus", this.preFocus, this);
9676 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9677 this.inputEl().on("keypress", this.filterKeys, this);
9679 this.inputEl().relayEvent('keypress', this);
9682 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9683 this.el.on("click", this.autoSize, this);
9686 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9687 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9690 if (typeof(this.before) == 'object') {
9691 this.before.render(this.el.select('.roo-input-before',true).first());
9693 if (typeof(this.after) == 'object') {
9694 this.after.render(this.el.select('.roo-input-after',true).first());
9697 this.inputEl().on('change', this.onChange, this);
9700 filterValidation : function(e){
9701 if(!e.isNavKeyPress()){
9702 this.validationTask.delay(this.validationDelay);
9706 * Validates the field value
9707 * @return {Boolean} True if the value is valid, else false
9709 validate : function(){
9710 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9711 if(this.disabled || this.validateValue(this.getRawValue())){
9722 * Validates a value according to the field's validation rules and marks the field as invalid
9723 * if the validation fails
9724 * @param {Mixed} value The value to validate
9725 * @return {Boolean} True if the value is valid, else false
9727 validateValue : function(value)
9729 if(this.getVisibilityEl().hasClass('hidden')){
9733 if(value.length < 1) { // if it's blank
9734 if(this.allowBlank){
9740 if(value.length < this.minLength){
9743 if(value.length > this.maxLength){
9747 var vt = Roo.form.VTypes;
9748 if(!vt[this.vtype](value, this)){
9752 if(typeof this.validator == "function"){
9753 var msg = this.validator(value);
9757 if (typeof(msg) == 'string') {
9758 this.invalidText = msg;
9762 if(this.regex && !this.regex.test(value)){
9770 fireKey : function(e){
9771 //Roo.log('field ' + e.getKey());
9772 if(e.isNavKeyPress()){
9773 this.fireEvent("specialkey", this, e);
9776 focus : function (selectText){
9778 this.inputEl().focus();
9779 if(selectText === true){
9780 this.inputEl().dom.select();
9786 onFocus : function(){
9787 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9788 // this.el.addClass(this.focusClass);
9791 this.hasFocus = true;
9792 this.startValue = this.getValue();
9793 this.fireEvent("focus", this);
9797 beforeBlur : Roo.emptyFn,
9801 onBlur : function(){
9803 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9804 //this.el.removeClass(this.focusClass);
9806 this.hasFocus = false;
9807 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9810 var v = this.getValue();
9811 if(String(v) !== String(this.startValue)){
9812 this.fireEvent('change', this, v, this.startValue);
9814 this.fireEvent("blur", this);
9817 onChange : function(e)
9819 var v = this.getValue();
9820 if(String(v) !== String(this.startValue)){
9821 this.fireEvent('change', this, v, this.startValue);
9827 * Resets the current field value to the originally loaded value and clears any validation messages
9830 this.setValue(this.originalValue);
9834 * Returns the name of the field
9835 * @return {Mixed} name The name field
9837 getName: function(){
9841 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9842 * @return {Mixed} value The field value
9844 getValue : function(){
9846 var v = this.inputEl().getValue();
9851 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9852 * @return {Mixed} value The field value
9854 getRawValue : function(){
9855 var v = this.inputEl().getValue();
9861 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9862 * @param {Mixed} value The value to set
9864 setRawValue : function(v){
9865 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9868 selectText : function(start, end){
9869 var v = this.getRawValue();
9871 start = start === undefined ? 0 : start;
9872 end = end === undefined ? v.length : end;
9873 var d = this.inputEl().dom;
9874 if(d.setSelectionRange){
9875 d.setSelectionRange(start, end);
9876 }else if(d.createTextRange){
9877 var range = d.createTextRange();
9878 range.moveStart("character", start);
9879 range.moveEnd("character", v.length-end);
9886 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9887 * @param {Mixed} value The value to set
9889 setValue : function(v){
9892 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9898 processValue : function(value){
9899 if(this.stripCharsRe){
9900 var newValue = value.replace(this.stripCharsRe, '');
9901 if(newValue !== value){
9902 this.setRawValue(newValue);
9909 preFocus : function(){
9911 if(this.selectOnFocus){
9912 this.inputEl().dom.select();
9915 filterKeys : function(e){
9917 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9920 var c = e.getCharCode(), cc = String.fromCharCode(c);
9921 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9924 if(!this.maskRe.test(cc)){
9929 * Clear any invalid styles/messages for this field
9931 clearInvalid : function(){
9933 if(!this.el || this.preventMark){ // not rendered
9938 this.el.removeClass([this.invalidClass, 'is-invalid']);
9940 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9942 var feedback = this.el.select('.form-control-feedback', true).first();
9945 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9951 this.indicator.removeClass('visible');
9952 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9955 this.fireEvent('valid', this);
9959 * Mark this field as valid
9961 markValid : function()
9963 if(!this.el || this.preventMark){ // not rendered...
9967 this.el.removeClass([this.invalidClass, this.validClass]);
9968 this.inputEl().removeClass(['is-valid', 'is-invalid']);
9970 var feedback = this.el.select('.form-control-feedback', true).first();
9973 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9977 this.indicator.removeClass('visible');
9978 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9985 if(this.allowBlank && !this.getRawValue().length){
9988 if (Roo.bootstrap.version == 3) {
9989 this.el.addClass(this.validClass);
9991 this.inputEl().addClass('is-valid');
9994 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9996 var feedback = this.el.select('.form-control-feedback', true).first();
9999 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10000 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10005 this.fireEvent('valid', this);
10009 * Mark this field as invalid
10010 * @param {String} msg The validation message
10012 markInvalid : function(msg)
10014 if(!this.el || this.preventMark){ // not rendered
10018 this.el.removeClass([this.invalidClass, this.validClass]);
10019 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10021 var feedback = this.el.select('.form-control-feedback', true).first();
10024 this.el.select('.form-control-feedback', true).first().removeClass(
10025 [this.invalidFeedbackClass, this.validFeedbackClass]);
10032 if(this.allowBlank && !this.getRawValue().length){
10036 if(this.indicator){
10037 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10038 this.indicator.addClass('visible');
10040 if (Roo.bootstrap.version == 3) {
10041 this.el.addClass(this.invalidClass);
10043 this.inputEl().addClass('is-invalid');
10048 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10050 var feedback = this.el.select('.form-control-feedback', true).first();
10053 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10055 if(this.getValue().length || this.forceFeedback){
10056 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10063 this.fireEvent('invalid', this, msg);
10066 SafariOnKeyDown : function(event)
10068 // this is a workaround for a password hang bug on chrome/ webkit.
10069 if (this.inputEl().dom.type != 'password') {
10073 var isSelectAll = false;
10075 if(this.inputEl().dom.selectionEnd > 0){
10076 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10078 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10079 event.preventDefault();
10084 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10086 event.preventDefault();
10087 // this is very hacky as keydown always get's upper case.
10089 var cc = String.fromCharCode(event.getCharCode());
10090 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
10094 adjustWidth : function(tag, w){
10095 tag = tag.toLowerCase();
10096 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10097 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10098 if(tag == 'input'){
10101 if(tag == 'textarea'){
10104 }else if(Roo.isOpera){
10105 if(tag == 'input'){
10108 if(tag == 'textarea'){
10116 setFieldLabel : function(v)
10118 if(!this.rendered){
10122 if(this.indicatorEl()){
10123 var ar = this.el.select('label > span',true);
10125 if (ar.elements.length) {
10126 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10127 this.fieldLabel = v;
10131 var br = this.el.select('label',true);
10133 if(br.elements.length) {
10134 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10135 this.fieldLabel = v;
10139 Roo.log('Cannot Found any of label > span || label in input');
10143 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10144 this.fieldLabel = v;
10159 * @class Roo.bootstrap.TextArea
10160 * @extends Roo.bootstrap.Input
10161 * Bootstrap TextArea class
10162 * @cfg {Number} cols Specifies the visible width of a text area
10163 * @cfg {Number} rows Specifies the visible number of lines in a text area
10164 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10165 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10166 * @cfg {string} html text
10169 * Create a new TextArea
10170 * @param {Object} config The config object
10173 Roo.bootstrap.TextArea = function(config){
10174 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10178 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
10188 getAutoCreate : function(){
10190 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10196 if(this.inputType != 'hidden'){
10197 cfg.cls = 'form-group' //input-group
10205 value : this.value || '',
10206 html: this.html || '',
10207 cls : 'form-control',
10208 placeholder : this.placeholder || ''
10212 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10213 input.maxLength = this.maxLength;
10217 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10221 input.cols = this.cols;
10224 if (this.readOnly) {
10225 input.readonly = true;
10229 input.name = this.name;
10233 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10237 ['xs','sm','md','lg'].map(function(size){
10238 if (settings[size]) {
10239 cfg.cls += ' col-' + size + '-' + settings[size];
10243 var inputblock = input;
10245 if(this.hasFeedback && !this.allowBlank){
10249 cls: 'glyphicon form-control-feedback'
10253 cls : 'has-feedback',
10262 if (this.before || this.after) {
10265 cls : 'input-group',
10269 inputblock.cn.push({
10271 cls : 'input-group-addon',
10276 inputblock.cn.push(input);
10278 if(this.hasFeedback && !this.allowBlank){
10279 inputblock.cls += ' has-feedback';
10280 inputblock.cn.push(feedback);
10284 inputblock.cn.push({
10286 cls : 'input-group-addon',
10293 if (align ==='left' && this.fieldLabel.length) {
10298 cls : 'control-label',
10299 html : this.fieldLabel
10310 if(this.labelWidth > 12){
10311 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10314 if(this.labelWidth < 13 && this.labelmd == 0){
10315 this.labelmd = this.labelWidth;
10318 if(this.labellg > 0){
10319 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10320 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10323 if(this.labelmd > 0){
10324 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10325 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10328 if(this.labelsm > 0){
10329 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10330 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10333 if(this.labelxs > 0){
10334 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10335 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10338 } else if ( this.fieldLabel.length) {
10343 //cls : 'input-group-addon',
10344 html : this.fieldLabel
10362 if (this.disabled) {
10363 input.disabled=true;
10370 * return the real textarea element.
10372 inputEl: function ()
10374 return this.el.select('textarea.form-control',true).first();
10378 * Clear any invalid styles/messages for this field
10380 clearInvalid : function()
10383 if(!this.el || this.preventMark){ // not rendered
10387 var label = this.el.select('label', true).first();
10388 var icon = this.el.select('i.fa-star', true).first();
10393 this.el.removeClass( this.validClass);
10394 this.inputEl().removeClass('is-invalid');
10396 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10398 var feedback = this.el.select('.form-control-feedback', true).first();
10401 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10406 this.fireEvent('valid', this);
10410 * Mark this field as valid
10412 markValid : function()
10414 if(!this.el || this.preventMark){ // not rendered
10418 this.el.removeClass([this.invalidClass, this.validClass]);
10419 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10421 var feedback = this.el.select('.form-control-feedback', true).first();
10424 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10427 if(this.disabled || this.allowBlank){
10431 var label = this.el.select('label', true).first();
10432 var icon = this.el.select('i.fa-star', true).first();
10437 if (Roo.bootstrap.version == 3) {
10438 this.el.addClass(this.validClass);
10440 this.inputEl().addClass('is-valid');
10444 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10446 var feedback = this.el.select('.form-control-feedback', true).first();
10449 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10450 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10455 this.fireEvent('valid', this);
10459 * Mark this field as invalid
10460 * @param {String} msg The validation message
10462 markInvalid : function(msg)
10464 if(!this.el || this.preventMark){ // not rendered
10468 this.el.removeClass([this.invalidClass, this.validClass]);
10469 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10471 var feedback = this.el.select('.form-control-feedback', true).first();
10474 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10477 if(this.disabled || this.allowBlank){
10481 var label = this.el.select('label', true).first();
10482 var icon = this.el.select('i.fa-star', true).first();
10484 if(!this.getValue().length && label && !icon){
10485 this.el.createChild({
10487 cls : 'text-danger fa fa-lg fa-star',
10488 tooltip : 'This field is required',
10489 style : 'margin-right:5px;'
10493 if (Roo.bootstrap.version == 3) {
10494 this.el.addClass(this.invalidClass);
10496 this.inputEl().addClass('is-invalid');
10499 // fixme ... this may be depricated need to test..
10500 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10502 var feedback = this.el.select('.form-control-feedback', true).first();
10505 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10507 if(this.getValue().length || this.forceFeedback){
10508 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10515 this.fireEvent('invalid', this, msg);
10523 * trigger field - base class for combo..
10528 * @class Roo.bootstrap.TriggerField
10529 * @extends Roo.bootstrap.Input
10530 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10531 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10532 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10533 * for which you can provide a custom implementation. For example:
10535 var trigger = new Roo.bootstrap.TriggerField();
10536 trigger.onTriggerClick = myTriggerFn;
10537 trigger.applyTo('my-field');
10540 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10541 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10542 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10543 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10544 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10547 * Create a new TriggerField.
10548 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10549 * to the base TextField)
10551 Roo.bootstrap.TriggerField = function(config){
10552 this.mimicing = false;
10553 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10556 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10558 * @cfg {String} triggerClass A CSS class to apply to the trigger
10561 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10566 * @cfg {Boolean} removable (true|false) special filter default false
10570 /** @cfg {Boolean} grow @hide */
10571 /** @cfg {Number} growMin @hide */
10572 /** @cfg {Number} growMax @hide */
10578 autoSize: Roo.emptyFn,
10582 deferHeight : true,
10585 actionMode : 'wrap',
10590 getAutoCreate : function(){
10592 var align = this.labelAlign || this.parentLabelAlign();
10597 cls: 'form-group' //input-group
10604 type : this.inputType,
10605 cls : 'form-control',
10606 autocomplete: 'new-password',
10607 placeholder : this.placeholder || ''
10611 input.name = this.name;
10614 input.cls += ' input-' + this.size;
10617 if (this.disabled) {
10618 input.disabled=true;
10621 var inputblock = input;
10623 if(this.hasFeedback && !this.allowBlank){
10627 cls: 'glyphicon form-control-feedback'
10630 if(this.removable && !this.editable && !this.tickable){
10632 cls : 'has-feedback',
10638 cls : 'roo-combo-removable-btn close'
10645 cls : 'has-feedback',
10654 if(this.removable && !this.editable && !this.tickable){
10656 cls : 'roo-removable',
10662 cls : 'roo-combo-removable-btn close'
10669 if (this.before || this.after) {
10672 cls : 'input-group',
10676 inputblock.cn.push({
10678 cls : 'input-group-addon input-group-prepend input-group-text',
10683 inputblock.cn.push(input);
10685 if(this.hasFeedback && !this.allowBlank){
10686 inputblock.cls += ' has-feedback';
10687 inputblock.cn.push(feedback);
10691 inputblock.cn.push({
10693 cls : 'input-group-addon input-group-append input-group-text',
10702 var ibwrap = inputblock;
10707 cls: 'roo-select2-choices',
10711 cls: 'roo-select2-search-field',
10723 cls: 'roo-select2-container input-group',
10728 cls: 'form-hidden-field'
10734 if(!this.multiple && this.showToggleBtn){
10740 if (this.caret != false) {
10743 cls: 'fa fa-' + this.caret
10750 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10752 Roo.bootstrap.version == 3 ? caret : '',
10755 cls: 'combobox-clear',
10769 combobox.cls += ' roo-select2-container-multi';
10773 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10774 tooltip : 'This field is required'
10776 if (Roo.bootstrap.version == 4) {
10779 style : 'display:none'
10784 if (align ==='left' && this.fieldLabel.length) {
10786 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10793 cls : 'control-label',
10794 html : this.fieldLabel
10806 var labelCfg = cfg.cn[1];
10807 var contentCfg = cfg.cn[2];
10809 if(this.indicatorpos == 'right'){
10814 cls : 'control-label',
10818 html : this.fieldLabel
10832 labelCfg = cfg.cn[0];
10833 contentCfg = cfg.cn[1];
10836 if(this.labelWidth > 12){
10837 labelCfg.style = "width: " + this.labelWidth + 'px';
10840 if(this.labelWidth < 13 && this.labelmd == 0){
10841 this.labelmd = this.labelWidth;
10844 if(this.labellg > 0){
10845 labelCfg.cls += ' col-lg-' + this.labellg;
10846 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10849 if(this.labelmd > 0){
10850 labelCfg.cls += ' col-md-' + this.labelmd;
10851 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10854 if(this.labelsm > 0){
10855 labelCfg.cls += ' col-sm-' + this.labelsm;
10856 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10859 if(this.labelxs > 0){
10860 labelCfg.cls += ' col-xs-' + this.labelxs;
10861 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10864 } else if ( this.fieldLabel.length) {
10865 // Roo.log(" label");
10870 //cls : 'input-group-addon',
10871 html : this.fieldLabel
10879 if(this.indicatorpos == 'right'){
10887 html : this.fieldLabel
10901 // Roo.log(" no label && no align");
10908 ['xs','sm','md','lg'].map(function(size){
10909 if (settings[size]) {
10910 cfg.cls += ' col-' + size + '-' + settings[size];
10921 onResize : function(w, h){
10922 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10923 // if(typeof w == 'number'){
10924 // var x = w - this.trigger.getWidth();
10925 // this.inputEl().setWidth(this.adjustWidth('input', x));
10926 // this.trigger.setStyle('left', x+'px');
10931 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10934 getResizeEl : function(){
10935 return this.inputEl();
10939 getPositionEl : function(){
10940 return this.inputEl();
10944 alignErrorIcon : function(){
10945 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10949 initEvents : function(){
10953 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10954 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10955 if(!this.multiple && this.showToggleBtn){
10956 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10957 if(this.hideTrigger){
10958 this.trigger.setDisplayed(false);
10960 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10964 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10967 if(this.removable && !this.editable && !this.tickable){
10968 var close = this.closeTriggerEl();
10971 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10972 close.on('click', this.removeBtnClick, this, close);
10976 //this.trigger.addClassOnOver('x-form-trigger-over');
10977 //this.trigger.addClassOnClick('x-form-trigger-click');
10980 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10984 closeTriggerEl : function()
10986 var close = this.el.select('.roo-combo-removable-btn', true).first();
10987 return close ? close : false;
10990 removeBtnClick : function(e, h, el)
10992 e.preventDefault();
10994 if(this.fireEvent("remove", this) !== false){
10996 this.fireEvent("afterremove", this)
11000 createList : function()
11002 this.list = Roo.get(document.body).createChild({
11003 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
11004 cls: 'typeahead typeahead-long dropdown-menu',
11005 style: 'display:none'
11008 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
11013 initTrigger : function(){
11018 onDestroy : function(){
11020 this.trigger.removeAllListeners();
11021 // this.trigger.remove();
11024 // this.wrap.remove();
11026 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
11030 onFocus : function(){
11031 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
11033 if(!this.mimicing){
11034 this.wrap.addClass('x-trigger-wrap-focus');
11035 this.mimicing = true;
11036 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
11037 if(this.monitorTab){
11038 this.el.on("keydown", this.checkTab, this);
11045 checkTab : function(e){
11046 if(e.getKey() == e.TAB){
11047 this.triggerBlur();
11052 onBlur : function(){
11057 mimicBlur : function(e, t){
11059 if(!this.wrap.contains(t) && this.validateBlur()){
11060 this.triggerBlur();
11066 triggerBlur : function(){
11067 this.mimicing = false;
11068 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
11069 if(this.monitorTab){
11070 this.el.un("keydown", this.checkTab, this);
11072 //this.wrap.removeClass('x-trigger-wrap-focus');
11073 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11077 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11078 validateBlur : function(e, t){
11083 onDisable : function(){
11084 this.inputEl().dom.disabled = true;
11085 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11087 // this.wrap.addClass('x-item-disabled');
11092 onEnable : function(){
11093 this.inputEl().dom.disabled = false;
11094 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11096 // this.el.removeClass('x-item-disabled');
11101 onShow : function(){
11102 var ae = this.getActionEl();
11105 ae.dom.style.display = '';
11106 ae.dom.style.visibility = 'visible';
11112 onHide : function(){
11113 var ae = this.getActionEl();
11114 ae.dom.style.display = 'none';
11118 * The function that should handle the trigger's click event. This method does nothing by default until overridden
11119 * by an implementing function.
11121 * @param {EventObject} e
11123 onTriggerClick : Roo.emptyFn
11127 * Ext JS Library 1.1.1
11128 * Copyright(c) 2006-2007, Ext JS, LLC.
11130 * Originally Released Under LGPL - original licence link has changed is not relivant.
11133 * <script type="text/javascript">
11138 * @class Roo.data.SortTypes
11140 * Defines the default sorting (casting?) comparison functions used when sorting data.
11142 Roo.data.SortTypes = {
11144 * Default sort that does nothing
11145 * @param {Mixed} s The value being converted
11146 * @return {Mixed} The comparison value
11148 none : function(s){
11153 * The regular expression used to strip tags
11157 stripTagsRE : /<\/?[^>]+>/gi,
11160 * Strips all HTML tags to sort on text only
11161 * @param {Mixed} s The value being converted
11162 * @return {String} The comparison value
11164 asText : function(s){
11165 return String(s).replace(this.stripTagsRE, "");
11169 * Strips all HTML tags to sort on text only - Case insensitive
11170 * @param {Mixed} s The value being converted
11171 * @return {String} The comparison value
11173 asUCText : function(s){
11174 return String(s).toUpperCase().replace(this.stripTagsRE, "");
11178 * Case insensitive string
11179 * @param {Mixed} s The value being converted
11180 * @return {String} The comparison value
11182 asUCString : function(s) {
11183 return String(s).toUpperCase();
11188 * @param {Mixed} s The value being converted
11189 * @return {Number} The comparison value
11191 asDate : function(s) {
11195 if(s instanceof Date){
11196 return s.getTime();
11198 return Date.parse(String(s));
11203 * @param {Mixed} s The value being converted
11204 * @return {Float} The comparison value
11206 asFloat : function(s) {
11207 var val = parseFloat(String(s).replace(/,/g, ""));
11216 * @param {Mixed} s The value being converted
11217 * @return {Number} The comparison value
11219 asInt : function(s) {
11220 var val = parseInt(String(s).replace(/,/g, ""));
11228 * Ext JS Library 1.1.1
11229 * Copyright(c) 2006-2007, Ext JS, LLC.
11231 * Originally Released Under LGPL - original licence link has changed is not relivant.
11234 * <script type="text/javascript">
11238 * @class Roo.data.Record
11239 * Instances of this class encapsulate both record <em>definition</em> information, and record
11240 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11241 * to access Records cached in an {@link Roo.data.Store} object.<br>
11243 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11244 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11247 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11249 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11250 * {@link #create}. The parameters are the same.
11251 * @param {Array} data An associative Array of data values keyed by the field name.
11252 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11253 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11254 * not specified an integer id is generated.
11256 Roo.data.Record = function(data, id){
11257 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11262 * Generate a constructor for a specific record layout.
11263 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11264 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11265 * Each field definition object may contain the following properties: <ul>
11266 * <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,
11267 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11268 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11269 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11270 * is being used, then this is a string containing the javascript expression to reference the data relative to
11271 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11272 * to the data item relative to the record element. If the mapping expression is the same as the field name,
11273 * this may be omitted.</p></li>
11274 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11275 * <ul><li>auto (Default, implies no conversion)</li>
11280 * <li>date</li></ul></p></li>
11281 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11282 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11283 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11284 * by the Reader into an object that will be stored in the Record. It is passed the
11285 * following parameters:<ul>
11286 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11288 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11290 * <br>usage:<br><pre><code>
11291 var TopicRecord = Roo.data.Record.create(
11292 {name: 'title', mapping: 'topic_title'},
11293 {name: 'author', mapping: 'username'},
11294 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11295 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11296 {name: 'lastPoster', mapping: 'user2'},
11297 {name: 'excerpt', mapping: 'post_text'}
11300 var myNewRecord = new TopicRecord({
11301 title: 'Do my job please',
11304 lastPost: new Date(),
11305 lastPoster: 'Animal',
11306 excerpt: 'No way dude!'
11308 myStore.add(myNewRecord);
11313 Roo.data.Record.create = function(o){
11314 var f = function(){
11315 f.superclass.constructor.apply(this, arguments);
11317 Roo.extend(f, Roo.data.Record);
11318 var p = f.prototype;
11319 p.fields = new Roo.util.MixedCollection(false, function(field){
11322 for(var i = 0, len = o.length; i < len; i++){
11323 p.fields.add(new Roo.data.Field(o[i]));
11325 f.getField = function(name){
11326 return p.fields.get(name);
11331 Roo.data.Record.AUTO_ID = 1000;
11332 Roo.data.Record.EDIT = 'edit';
11333 Roo.data.Record.REJECT = 'reject';
11334 Roo.data.Record.COMMIT = 'commit';
11336 Roo.data.Record.prototype = {
11338 * Readonly flag - true if this record has been modified.
11347 join : function(store){
11348 this.store = store;
11352 * Set the named field to the specified value.
11353 * @param {String} name The name of the field to set.
11354 * @param {Object} value The value to set the field to.
11356 set : function(name, value){
11357 if(this.data[name] == value){
11361 if(!this.modified){
11362 this.modified = {};
11364 if(typeof this.modified[name] == 'undefined'){
11365 this.modified[name] = this.data[name];
11367 this.data[name] = value;
11368 if(!this.editing && this.store){
11369 this.store.afterEdit(this);
11374 * Get the value of the named field.
11375 * @param {String} name The name of the field to get the value of.
11376 * @return {Object} The value of the field.
11378 get : function(name){
11379 return this.data[name];
11383 beginEdit : function(){
11384 this.editing = true;
11385 this.modified = {};
11389 cancelEdit : function(){
11390 this.editing = false;
11391 delete this.modified;
11395 endEdit : function(){
11396 this.editing = false;
11397 if(this.dirty && this.store){
11398 this.store.afterEdit(this);
11403 * Usually called by the {@link Roo.data.Store} which owns the Record.
11404 * Rejects all changes made to the Record since either creation, or the last commit operation.
11405 * Modified fields are reverted to their original values.
11407 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11408 * of reject operations.
11410 reject : function(){
11411 var m = this.modified;
11413 if(typeof m[n] != "function"){
11414 this.data[n] = m[n];
11417 this.dirty = false;
11418 delete this.modified;
11419 this.editing = false;
11421 this.store.afterReject(this);
11426 * Usually called by the {@link Roo.data.Store} which owns the Record.
11427 * Commits all changes made to the Record since either creation, or the last commit operation.
11429 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11430 * of commit operations.
11432 commit : function(){
11433 this.dirty = false;
11434 delete this.modified;
11435 this.editing = false;
11437 this.store.afterCommit(this);
11442 hasError : function(){
11443 return this.error != null;
11447 clearError : function(){
11452 * Creates a copy of this record.
11453 * @param {String} id (optional) A new record id if you don't want to use this record's id
11456 copy : function(newId) {
11457 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11461 * Ext JS Library 1.1.1
11462 * Copyright(c) 2006-2007, Ext JS, LLC.
11464 * Originally Released Under LGPL - original licence link has changed is not relivant.
11467 * <script type="text/javascript">
11473 * @class Roo.data.Store
11474 * @extends Roo.util.Observable
11475 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11476 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11478 * 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
11479 * has no knowledge of the format of the data returned by the Proxy.<br>
11481 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11482 * instances from the data object. These records are cached and made available through accessor functions.
11484 * Creates a new Store.
11485 * @param {Object} config A config object containing the objects needed for the Store to access data,
11486 * and read the data into Records.
11488 Roo.data.Store = function(config){
11489 this.data = new Roo.util.MixedCollection(false);
11490 this.data.getKey = function(o){
11493 this.baseParams = {};
11495 this.paramNames = {
11500 "multisort" : "_multisort"
11503 if(config && config.data){
11504 this.inlineData = config.data;
11505 delete config.data;
11508 Roo.apply(this, config);
11510 if(this.reader){ // reader passed
11511 this.reader = Roo.factory(this.reader, Roo.data);
11512 this.reader.xmodule = this.xmodule || false;
11513 if(!this.recordType){
11514 this.recordType = this.reader.recordType;
11516 if(this.reader.onMetaChange){
11517 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11521 if(this.recordType){
11522 this.fields = this.recordType.prototype.fields;
11524 this.modified = [];
11528 * @event datachanged
11529 * Fires when the data cache has changed, and a widget which is using this Store
11530 * as a Record cache should refresh its view.
11531 * @param {Store} this
11533 datachanged : true,
11535 * @event metachange
11536 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11537 * @param {Store} this
11538 * @param {Object} meta The JSON metadata
11543 * Fires when Records have been added to the Store
11544 * @param {Store} this
11545 * @param {Roo.data.Record[]} records The array of Records added
11546 * @param {Number} index The index at which the record(s) were added
11551 * Fires when a Record has been removed from the Store
11552 * @param {Store} this
11553 * @param {Roo.data.Record} record The Record that was removed
11554 * @param {Number} index The index at which the record was removed
11559 * Fires when a Record has been updated
11560 * @param {Store} this
11561 * @param {Roo.data.Record} record The Record that was updated
11562 * @param {String} operation The update operation being performed. Value may be one of:
11564 Roo.data.Record.EDIT
11565 Roo.data.Record.REJECT
11566 Roo.data.Record.COMMIT
11572 * Fires when the data cache has been cleared.
11573 * @param {Store} this
11577 * @event beforeload
11578 * Fires before a request is made for a new data object. If the beforeload handler returns false
11579 * the load action will be canceled.
11580 * @param {Store} this
11581 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11585 * @event beforeloadadd
11586 * Fires after a new set of Records has been loaded.
11587 * @param {Store} this
11588 * @param {Roo.data.Record[]} records The Records that were loaded
11589 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11591 beforeloadadd : true,
11594 * Fires after a new set of Records has been loaded, before they are added to the store.
11595 * @param {Store} this
11596 * @param {Roo.data.Record[]} records The Records that were loaded
11597 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11598 * @params {Object} return from reader
11602 * @event loadexception
11603 * Fires if an exception occurs in the Proxy during loading.
11604 * Called with the signature of the Proxy's "loadexception" event.
11605 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11608 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11609 * @param {Object} load options
11610 * @param {Object} jsonData from your request (normally this contains the Exception)
11612 loadexception : true
11616 this.proxy = Roo.factory(this.proxy, Roo.data);
11617 this.proxy.xmodule = this.xmodule || false;
11618 this.relayEvents(this.proxy, ["loadexception"]);
11620 this.sortToggle = {};
11621 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11623 Roo.data.Store.superclass.constructor.call(this);
11625 if(this.inlineData){
11626 this.loadData(this.inlineData);
11627 delete this.inlineData;
11631 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11633 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11634 * without a remote query - used by combo/forms at present.
11638 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11641 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11644 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11645 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11648 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11649 * on any HTTP request
11652 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11655 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11659 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11660 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11662 remoteSort : false,
11665 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11666 * loaded or when a record is removed. (defaults to false).
11668 pruneModifiedRecords : false,
11671 lastOptions : null,
11674 * Add Records to the Store and fires the add event.
11675 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11677 add : function(records){
11678 records = [].concat(records);
11679 for(var i = 0, len = records.length; i < len; i++){
11680 records[i].join(this);
11682 var index = this.data.length;
11683 this.data.addAll(records);
11684 this.fireEvent("add", this, records, index);
11688 * Remove a Record from the Store and fires the remove event.
11689 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11691 remove : function(record){
11692 var index = this.data.indexOf(record);
11693 this.data.removeAt(index);
11695 if(this.pruneModifiedRecords){
11696 this.modified.remove(record);
11698 this.fireEvent("remove", this, record, index);
11702 * Remove all Records from the Store and fires the clear event.
11704 removeAll : function(){
11706 if(this.pruneModifiedRecords){
11707 this.modified = [];
11709 this.fireEvent("clear", this);
11713 * Inserts Records to the Store at the given index and fires the add event.
11714 * @param {Number} index The start index at which to insert the passed Records.
11715 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11717 insert : function(index, records){
11718 records = [].concat(records);
11719 for(var i = 0, len = records.length; i < len; i++){
11720 this.data.insert(index, records[i]);
11721 records[i].join(this);
11723 this.fireEvent("add", this, records, index);
11727 * Get the index within the cache of the passed Record.
11728 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11729 * @return {Number} The index of the passed Record. Returns -1 if not found.
11731 indexOf : function(record){
11732 return this.data.indexOf(record);
11736 * Get the index within the cache of the Record with the passed id.
11737 * @param {String} id The id of the Record to find.
11738 * @return {Number} The index of the Record. Returns -1 if not found.
11740 indexOfId : function(id){
11741 return this.data.indexOfKey(id);
11745 * Get the Record with the specified id.
11746 * @param {String} id The id of the Record to find.
11747 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11749 getById : function(id){
11750 return this.data.key(id);
11754 * Get the Record at the specified index.
11755 * @param {Number} index The index of the Record to find.
11756 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11758 getAt : function(index){
11759 return this.data.itemAt(index);
11763 * Returns a range of Records between specified indices.
11764 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11765 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11766 * @return {Roo.data.Record[]} An array of Records
11768 getRange : function(start, end){
11769 return this.data.getRange(start, end);
11773 storeOptions : function(o){
11774 o = Roo.apply({}, o);
11777 this.lastOptions = o;
11781 * Loads the Record cache from the configured Proxy using the configured Reader.
11783 * If using remote paging, then the first load call must specify the <em>start</em>
11784 * and <em>limit</em> properties in the options.params property to establish the initial
11785 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11787 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11788 * and this call will return before the new data has been loaded. Perform any post-processing
11789 * in a callback function, or in a "load" event handler.</strong>
11791 * @param {Object} options An object containing properties which control loading options:<ul>
11792 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11793 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11794 * passed the following arguments:<ul>
11795 * <li>r : Roo.data.Record[]</li>
11796 * <li>options: Options object from the load call</li>
11797 * <li>success: Boolean success indicator</li></ul></li>
11798 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11799 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11802 load : function(options){
11803 options = options || {};
11804 if(this.fireEvent("beforeload", this, options) !== false){
11805 this.storeOptions(options);
11806 var p = Roo.apply(options.params || {}, this.baseParams);
11807 // if meta was not loaded from remote source.. try requesting it.
11808 if (!this.reader.metaFromRemote) {
11809 p._requestMeta = 1;
11811 if(this.sortInfo && this.remoteSort){
11812 var pn = this.paramNames;
11813 p[pn["sort"]] = this.sortInfo.field;
11814 p[pn["dir"]] = this.sortInfo.direction;
11816 if (this.multiSort) {
11817 var pn = this.paramNames;
11818 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11821 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11826 * Reloads the Record cache from the configured Proxy using the configured Reader and
11827 * the options from the last load operation performed.
11828 * @param {Object} options (optional) An object containing properties which may override the options
11829 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11830 * the most recently used options are reused).
11832 reload : function(options){
11833 this.load(Roo.applyIf(options||{}, this.lastOptions));
11837 // Called as a callback by the Reader during a load operation.
11838 loadRecords : function(o, options, success){
11839 if(!o || success === false){
11840 if(success !== false){
11841 this.fireEvent("load", this, [], options, o);
11843 if(options.callback){
11844 options.callback.call(options.scope || this, [], options, false);
11848 // if data returned failure - throw an exception.
11849 if (o.success === false) {
11850 // show a message if no listener is registered.
11851 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11852 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11854 // loadmask wil be hooked into this..
11855 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11858 var r = o.records, t = o.totalRecords || r.length;
11860 this.fireEvent("beforeloadadd", this, r, options, o);
11862 if(!options || options.add !== true){
11863 if(this.pruneModifiedRecords){
11864 this.modified = [];
11866 for(var i = 0, len = r.length; i < len; i++){
11870 this.data = this.snapshot;
11871 delete this.snapshot;
11874 this.data.addAll(r);
11875 this.totalLength = t;
11877 this.fireEvent("datachanged", this);
11879 this.totalLength = Math.max(t, this.data.length+r.length);
11883 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11885 var e = new Roo.data.Record({});
11887 e.set(this.parent.displayField, this.parent.emptyTitle);
11888 e.set(this.parent.valueField, '');
11893 this.fireEvent("load", this, r, options, o);
11894 if(options.callback){
11895 options.callback.call(options.scope || this, r, options, true);
11901 * Loads data from a passed data block. A Reader which understands the format of the data
11902 * must have been configured in the constructor.
11903 * @param {Object} data The data block from which to read the Records. The format of the data expected
11904 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11905 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11907 loadData : function(o, append){
11908 var r = this.reader.readRecords(o);
11909 this.loadRecords(r, {add: append}, true);
11913 * Gets the number of cached records.
11915 * <em>If using paging, this may not be the total size of the dataset. If the data object
11916 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11917 * the data set size</em>
11919 getCount : function(){
11920 return this.data.length || 0;
11924 * Gets the total number of records in the dataset as returned by the server.
11926 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11927 * the dataset size</em>
11929 getTotalCount : function(){
11930 return this.totalLength || 0;
11934 * Returns the sort state of the Store as an object with two properties:
11936 field {String} The name of the field by which the Records are sorted
11937 direction {String} The sort order, "ASC" or "DESC"
11940 getSortState : function(){
11941 return this.sortInfo;
11945 applySort : function(){
11946 if(this.sortInfo && !this.remoteSort){
11947 var s = this.sortInfo, f = s.field;
11948 var st = this.fields.get(f).sortType;
11949 var fn = function(r1, r2){
11950 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11951 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11953 this.data.sort(s.direction, fn);
11954 if(this.snapshot && this.snapshot != this.data){
11955 this.snapshot.sort(s.direction, fn);
11961 * Sets the default sort column and order to be used by the next load operation.
11962 * @param {String} fieldName The name of the field to sort by.
11963 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11965 setDefaultSort : function(field, dir){
11966 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11970 * Sort the Records.
11971 * If remote sorting is used, the sort is performed on the server, and the cache is
11972 * reloaded. If local sorting is used, the cache is sorted internally.
11973 * @param {String} fieldName The name of the field to sort by.
11974 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11976 sort : function(fieldName, dir){
11977 var f = this.fields.get(fieldName);
11979 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11981 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11982 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11987 this.sortToggle[f.name] = dir;
11988 this.sortInfo = {field: f.name, direction: dir};
11989 if(!this.remoteSort){
11991 this.fireEvent("datachanged", this);
11993 this.load(this.lastOptions);
11998 * Calls the specified function for each of the Records in the cache.
11999 * @param {Function} fn The function to call. The Record is passed as the first parameter.
12000 * Returning <em>false</em> aborts and exits the iteration.
12001 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
12003 each : function(fn, scope){
12004 this.data.each(fn, scope);
12008 * Gets all records modified since the last commit. Modified records are persisted across load operations
12009 * (e.g., during paging).
12010 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
12012 getModifiedRecords : function(){
12013 return this.modified;
12017 createFilterFn : function(property, value, anyMatch){
12018 if(!value.exec){ // not a regex
12019 value = String(value);
12020 if(value.length == 0){
12023 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
12025 return function(r){
12026 return value.test(r.data[property]);
12031 * Sums the value of <i>property</i> for each record between start and end and returns the result.
12032 * @param {String} property A field on your records
12033 * @param {Number} start The record index to start at (defaults to 0)
12034 * @param {Number} end The last record index to include (defaults to length - 1)
12035 * @return {Number} The sum
12037 sum : function(property, start, end){
12038 var rs = this.data.items, v = 0;
12039 start = start || 0;
12040 end = (end || end === 0) ? end : rs.length-1;
12042 for(var i = start; i <= end; i++){
12043 v += (rs[i].data[property] || 0);
12049 * Filter the records by a specified property.
12050 * @param {String} field A field on your records
12051 * @param {String/RegExp} value Either a string that the field
12052 * should start with or a RegExp to test against the field
12053 * @param {Boolean} anyMatch True to match any part not just the beginning
12055 filter : function(property, value, anyMatch){
12056 var fn = this.createFilterFn(property, value, anyMatch);
12057 return fn ? this.filterBy(fn) : this.clearFilter();
12061 * Filter by a function. The specified function will be called with each
12062 * record in this data source. If the function returns true the record is included,
12063 * otherwise it is filtered.
12064 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12065 * @param {Object} scope (optional) The scope of the function (defaults to this)
12067 filterBy : function(fn, scope){
12068 this.snapshot = this.snapshot || this.data;
12069 this.data = this.queryBy(fn, scope||this);
12070 this.fireEvent("datachanged", this);
12074 * Query the records by a specified property.
12075 * @param {String} field A field on your records
12076 * @param {String/RegExp} value Either a string that the field
12077 * should start with or a RegExp to test against the field
12078 * @param {Boolean} anyMatch True to match any part not just the beginning
12079 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12081 query : function(property, value, anyMatch){
12082 var fn = this.createFilterFn(property, value, anyMatch);
12083 return fn ? this.queryBy(fn) : this.data.clone();
12087 * Query by a function. The specified function will be called with each
12088 * record in this data source. If the function returns true the record is included
12090 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12091 * @param {Object} scope (optional) The scope of the function (defaults to this)
12092 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12094 queryBy : function(fn, scope){
12095 var data = this.snapshot || this.data;
12096 return data.filterBy(fn, scope||this);
12100 * Collects unique values for a particular dataIndex from this store.
12101 * @param {String} dataIndex The property to collect
12102 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12103 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12104 * @return {Array} An array of the unique values
12106 collect : function(dataIndex, allowNull, bypassFilter){
12107 var d = (bypassFilter === true && this.snapshot) ?
12108 this.snapshot.items : this.data.items;
12109 var v, sv, r = [], l = {};
12110 for(var i = 0, len = d.length; i < len; i++){
12111 v = d[i].data[dataIndex];
12113 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12122 * Revert to a view of the Record cache with no filtering applied.
12123 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12125 clearFilter : function(suppressEvent){
12126 if(this.snapshot && this.snapshot != this.data){
12127 this.data = this.snapshot;
12128 delete this.snapshot;
12129 if(suppressEvent !== true){
12130 this.fireEvent("datachanged", this);
12136 afterEdit : function(record){
12137 if(this.modified.indexOf(record) == -1){
12138 this.modified.push(record);
12140 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12144 afterReject : function(record){
12145 this.modified.remove(record);
12146 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12150 afterCommit : function(record){
12151 this.modified.remove(record);
12152 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12156 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12157 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12159 commitChanges : function(){
12160 var m = this.modified.slice(0);
12161 this.modified = [];
12162 for(var i = 0, len = m.length; i < len; i++){
12168 * Cancel outstanding changes on all changed records.
12170 rejectChanges : function(){
12171 var m = this.modified.slice(0);
12172 this.modified = [];
12173 for(var i = 0, len = m.length; i < len; i++){
12178 onMetaChange : function(meta, rtype, o){
12179 this.recordType = rtype;
12180 this.fields = rtype.prototype.fields;
12181 delete this.snapshot;
12182 this.sortInfo = meta.sortInfo || this.sortInfo;
12183 this.modified = [];
12184 this.fireEvent('metachange', this, this.reader.meta);
12187 moveIndex : function(data, type)
12189 var index = this.indexOf(data);
12191 var newIndex = index + type;
12195 this.insert(newIndex, data);
12200 * Ext JS Library 1.1.1
12201 * Copyright(c) 2006-2007, Ext JS, LLC.
12203 * Originally Released Under LGPL - original licence link has changed is not relivant.
12206 * <script type="text/javascript">
12210 * @class Roo.data.SimpleStore
12211 * @extends Roo.data.Store
12212 * Small helper class to make creating Stores from Array data easier.
12213 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12214 * @cfg {Array} fields An array of field definition objects, or field name strings.
12215 * @cfg {Array} data The multi-dimensional array of data
12217 * @param {Object} config
12219 Roo.data.SimpleStore = function(config){
12220 Roo.data.SimpleStore.superclass.constructor.call(this, {
12222 reader: new Roo.data.ArrayReader({
12225 Roo.data.Record.create(config.fields)
12227 proxy : new Roo.data.MemoryProxy(config.data)
12231 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12233 * Ext JS Library 1.1.1
12234 * Copyright(c) 2006-2007, Ext JS, LLC.
12236 * Originally Released Under LGPL - original licence link has changed is not relivant.
12239 * <script type="text/javascript">
12244 * @extends Roo.data.Store
12245 * @class Roo.data.JsonStore
12246 * Small helper class to make creating Stores for JSON data easier. <br/>
12248 var store = new Roo.data.JsonStore({
12249 url: 'get-images.php',
12251 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12254 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12255 * JsonReader and HttpProxy (unless inline data is provided).</b>
12256 * @cfg {Array} fields An array of field definition objects, or field name strings.
12258 * @param {Object} config
12260 Roo.data.JsonStore = function(c){
12261 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12262 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12263 reader: new Roo.data.JsonReader(c, c.fields)
12266 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12268 * Ext JS Library 1.1.1
12269 * Copyright(c) 2006-2007, Ext JS, LLC.
12271 * Originally Released Under LGPL - original licence link has changed is not relivant.
12274 * <script type="text/javascript">
12278 Roo.data.Field = function(config){
12279 if(typeof config == "string"){
12280 config = {name: config};
12282 Roo.apply(this, config);
12285 this.type = "auto";
12288 var st = Roo.data.SortTypes;
12289 // named sortTypes are supported, here we look them up
12290 if(typeof this.sortType == "string"){
12291 this.sortType = st[this.sortType];
12294 // set default sortType for strings and dates
12295 if(!this.sortType){
12298 this.sortType = st.asUCString;
12301 this.sortType = st.asDate;
12304 this.sortType = st.none;
12309 var stripRe = /[\$,%]/g;
12311 // prebuilt conversion function for this field, instead of
12312 // switching every time we're reading a value
12314 var cv, dateFormat = this.dateFormat;
12319 cv = function(v){ return v; };
12322 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12326 return v !== undefined && v !== null && v !== '' ?
12327 parseInt(String(v).replace(stripRe, ""), 10) : '';
12332 return v !== undefined && v !== null && v !== '' ?
12333 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12338 cv = function(v){ return v === true || v === "true" || v == 1; };
12345 if(v instanceof Date){
12349 if(dateFormat == "timestamp"){
12350 return new Date(v*1000);
12352 return Date.parseDate(v, dateFormat);
12354 var parsed = Date.parse(v);
12355 return parsed ? new Date(parsed) : null;
12364 Roo.data.Field.prototype = {
12372 * Ext JS Library 1.1.1
12373 * Copyright(c) 2006-2007, Ext JS, LLC.
12375 * Originally Released Under LGPL - original licence link has changed is not relivant.
12378 * <script type="text/javascript">
12381 // Base class for reading structured data from a data source. This class is intended to be
12382 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12385 * @class Roo.data.DataReader
12386 * Base class for reading structured data from a data source. This class is intended to be
12387 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12390 Roo.data.DataReader = function(meta, recordType){
12394 this.recordType = recordType instanceof Array ?
12395 Roo.data.Record.create(recordType) : recordType;
12398 Roo.data.DataReader.prototype = {
12400 * Create an empty record
12401 * @param {Object} data (optional) - overlay some values
12402 * @return {Roo.data.Record} record created.
12404 newRow : function(d) {
12406 this.recordType.prototype.fields.each(function(c) {
12408 case 'int' : da[c.name] = 0; break;
12409 case 'date' : da[c.name] = new Date(); break;
12410 case 'float' : da[c.name] = 0.0; break;
12411 case 'boolean' : da[c.name] = false; break;
12412 default : da[c.name] = ""; break;
12416 return new this.recordType(Roo.apply(da, d));
12421 * Ext JS Library 1.1.1
12422 * Copyright(c) 2006-2007, Ext JS, LLC.
12424 * Originally Released Under LGPL - original licence link has changed is not relivant.
12427 * <script type="text/javascript">
12431 * @class Roo.data.DataProxy
12432 * @extends Roo.data.Observable
12433 * This class is an abstract base class for implementations which provide retrieval of
12434 * unformatted data objects.<br>
12436 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12437 * (of the appropriate type which knows how to parse the data object) to provide a block of
12438 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12440 * Custom implementations must implement the load method as described in
12441 * {@link Roo.data.HttpProxy#load}.
12443 Roo.data.DataProxy = function(){
12446 * @event beforeload
12447 * Fires before a network request is made to retrieve a data object.
12448 * @param {Object} This DataProxy object.
12449 * @param {Object} params The params parameter to the load function.
12454 * Fires before the load method's callback is called.
12455 * @param {Object} This DataProxy object.
12456 * @param {Object} o The data object.
12457 * @param {Object} arg The callback argument object passed to the load function.
12461 * @event loadexception
12462 * Fires if an Exception occurs during data retrieval.
12463 * @param {Object} This DataProxy object.
12464 * @param {Object} o The data object.
12465 * @param {Object} arg The callback argument object passed to the load function.
12466 * @param {Object} e The Exception.
12468 loadexception : true
12470 Roo.data.DataProxy.superclass.constructor.call(this);
12473 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12476 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12480 * Ext JS Library 1.1.1
12481 * Copyright(c) 2006-2007, Ext JS, LLC.
12483 * Originally Released Under LGPL - original licence link has changed is not relivant.
12486 * <script type="text/javascript">
12489 * @class Roo.data.MemoryProxy
12490 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12491 * to the Reader when its load method is called.
12493 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12495 Roo.data.MemoryProxy = function(data){
12499 Roo.data.MemoryProxy.superclass.constructor.call(this);
12503 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12506 * Load data from the requested source (in this case an in-memory
12507 * data object passed to the constructor), read the data object into
12508 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12509 * process that block using the passed callback.
12510 * @param {Object} params This parameter is not used by the MemoryProxy class.
12511 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12512 * object into a block of Roo.data.Records.
12513 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12514 * The function must be passed <ul>
12515 * <li>The Record block object</li>
12516 * <li>The "arg" argument from the load function</li>
12517 * <li>A boolean success indicator</li>
12519 * @param {Object} scope The scope in which to call the callback
12520 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12522 load : function(params, reader, callback, scope, arg){
12523 params = params || {};
12526 result = reader.readRecords(params.data ? params.data :this.data);
12528 this.fireEvent("loadexception", this, arg, null, e);
12529 callback.call(scope, null, arg, false);
12532 callback.call(scope, result, arg, true);
12536 update : function(params, records){
12541 * Ext JS Library 1.1.1
12542 * Copyright(c) 2006-2007, Ext JS, LLC.
12544 * Originally Released Under LGPL - original licence link has changed is not relivant.
12547 * <script type="text/javascript">
12550 * @class Roo.data.HttpProxy
12551 * @extends Roo.data.DataProxy
12552 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12553 * configured to reference a certain URL.<br><br>
12555 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12556 * from which the running page was served.<br><br>
12558 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12560 * Be aware that to enable the browser to parse an XML document, the server must set
12561 * the Content-Type header in the HTTP response to "text/xml".
12563 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12564 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12565 * will be used to make the request.
12567 Roo.data.HttpProxy = function(conn){
12568 Roo.data.HttpProxy.superclass.constructor.call(this);
12569 // is conn a conn config or a real conn?
12571 this.useAjax = !conn || !conn.events;
12575 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12576 // thse are take from connection...
12579 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12582 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12583 * extra parameters to each request made by this object. (defaults to undefined)
12586 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12587 * to each request made by this object. (defaults to undefined)
12590 * @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)
12593 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12596 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12602 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12606 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12607 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12608 * a finer-grained basis than the DataProxy events.
12610 getConnection : function(){
12611 return this.useAjax ? Roo.Ajax : this.conn;
12615 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12616 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12617 * process that block using the passed callback.
12618 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12619 * for the request to the remote server.
12620 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12621 * object into a block of Roo.data.Records.
12622 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12623 * The function must be passed <ul>
12624 * <li>The Record block object</li>
12625 * <li>The "arg" argument from the load function</li>
12626 * <li>A boolean success indicator</li>
12628 * @param {Object} scope The scope in which to call the callback
12629 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12631 load : function(params, reader, callback, scope, arg){
12632 if(this.fireEvent("beforeload", this, params) !== false){
12634 params : params || {},
12636 callback : callback,
12641 callback : this.loadResponse,
12645 Roo.applyIf(o, this.conn);
12646 if(this.activeRequest){
12647 Roo.Ajax.abort(this.activeRequest);
12649 this.activeRequest = Roo.Ajax.request(o);
12651 this.conn.request(o);
12654 callback.call(scope||this, null, arg, false);
12659 loadResponse : function(o, success, response){
12660 delete this.activeRequest;
12662 this.fireEvent("loadexception", this, o, response);
12663 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12668 result = o.reader.read(response);
12670 this.fireEvent("loadexception", this, o, response, e);
12671 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12675 this.fireEvent("load", this, o, o.request.arg);
12676 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12680 update : function(dataSet){
12685 updateResponse : function(dataSet){
12690 * Ext JS Library 1.1.1
12691 * Copyright(c) 2006-2007, Ext JS, LLC.
12693 * Originally Released Under LGPL - original licence link has changed is not relivant.
12696 * <script type="text/javascript">
12700 * @class Roo.data.ScriptTagProxy
12701 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12702 * other than the originating domain of the running page.<br><br>
12704 * <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
12705 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12707 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12708 * source code that is used as the source inside a <script> tag.<br><br>
12710 * In order for the browser to process the returned data, the server must wrap the data object
12711 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12712 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12713 * depending on whether the callback name was passed:
12716 boolean scriptTag = false;
12717 String cb = request.getParameter("callback");
12720 response.setContentType("text/javascript");
12722 response.setContentType("application/x-json");
12724 Writer out = response.getWriter();
12726 out.write(cb + "(");
12728 out.print(dataBlock.toJsonString());
12735 * @param {Object} config A configuration object.
12737 Roo.data.ScriptTagProxy = function(config){
12738 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12739 Roo.apply(this, config);
12740 this.head = document.getElementsByTagName("head")[0];
12743 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12745 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12747 * @cfg {String} url The URL from which to request the data object.
12750 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12754 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12755 * the server the name of the callback function set up by the load call to process the returned data object.
12756 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12757 * javascript output which calls this named function passing the data object as its only parameter.
12759 callbackParam : "callback",
12761 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12762 * name to the request.
12767 * Load data from the configured URL, read the data object into
12768 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12769 * process that block using the passed callback.
12770 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12771 * for the request to the remote server.
12772 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12773 * object into a block of Roo.data.Records.
12774 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12775 * The function must be passed <ul>
12776 * <li>The Record block object</li>
12777 * <li>The "arg" argument from the load function</li>
12778 * <li>A boolean success indicator</li>
12780 * @param {Object} scope The scope in which to call the callback
12781 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12783 load : function(params, reader, callback, scope, arg){
12784 if(this.fireEvent("beforeload", this, params) !== false){
12786 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12788 var url = this.url;
12789 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12791 url += "&_dc=" + (new Date().getTime());
12793 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12796 cb : "stcCallback"+transId,
12797 scriptId : "stcScript"+transId,
12801 callback : callback,
12807 window[trans.cb] = function(o){
12808 conn.handleResponse(o, trans);
12811 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12813 if(this.autoAbort !== false){
12817 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12819 var script = document.createElement("script");
12820 script.setAttribute("src", url);
12821 script.setAttribute("type", "text/javascript");
12822 script.setAttribute("id", trans.scriptId);
12823 this.head.appendChild(script);
12825 this.trans = trans;
12827 callback.call(scope||this, null, arg, false);
12832 isLoading : function(){
12833 return this.trans ? true : false;
12837 * Abort the current server request.
12839 abort : function(){
12840 if(this.isLoading()){
12841 this.destroyTrans(this.trans);
12846 destroyTrans : function(trans, isLoaded){
12847 this.head.removeChild(document.getElementById(trans.scriptId));
12848 clearTimeout(trans.timeoutId);
12850 window[trans.cb] = undefined;
12852 delete window[trans.cb];
12855 // if hasn't been loaded, wait for load to remove it to prevent script error
12856 window[trans.cb] = function(){
12857 window[trans.cb] = undefined;
12859 delete window[trans.cb];
12866 handleResponse : function(o, trans){
12867 this.trans = false;
12868 this.destroyTrans(trans, true);
12871 result = trans.reader.readRecords(o);
12873 this.fireEvent("loadexception", this, o, trans.arg, e);
12874 trans.callback.call(trans.scope||window, null, trans.arg, false);
12877 this.fireEvent("load", this, o, trans.arg);
12878 trans.callback.call(trans.scope||window, result, trans.arg, true);
12882 handleFailure : function(trans){
12883 this.trans = false;
12884 this.destroyTrans(trans, false);
12885 this.fireEvent("loadexception", this, null, trans.arg);
12886 trans.callback.call(trans.scope||window, null, trans.arg, false);
12890 * Ext JS Library 1.1.1
12891 * Copyright(c) 2006-2007, Ext JS, LLC.
12893 * Originally Released Under LGPL - original licence link has changed is not relivant.
12896 * <script type="text/javascript">
12900 * @class Roo.data.JsonReader
12901 * @extends Roo.data.DataReader
12902 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12903 * based on mappings in a provided Roo.data.Record constructor.
12905 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12906 * in the reply previously.
12911 var RecordDef = Roo.data.Record.create([
12912 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12913 {name: 'occupation'} // This field will use "occupation" as the mapping.
12915 var myReader = new Roo.data.JsonReader({
12916 totalProperty: "results", // The property which contains the total dataset size (optional)
12917 root: "rows", // The property which contains an Array of row objects
12918 id: "id" // The property within each row object that provides an ID for the record (optional)
12922 * This would consume a JSON file like this:
12924 { 'results': 2, 'rows': [
12925 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12926 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12929 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12930 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12931 * paged from the remote server.
12932 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12933 * @cfg {String} root name of the property which contains the Array of row objects.
12934 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12935 * @cfg {Array} fields Array of field definition objects
12937 * Create a new JsonReader
12938 * @param {Object} meta Metadata configuration options
12939 * @param {Object} recordType Either an Array of field definition objects,
12940 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12942 Roo.data.JsonReader = function(meta, recordType){
12945 // set some defaults:
12946 Roo.applyIf(meta, {
12947 totalProperty: 'total',
12948 successProperty : 'success',
12953 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12955 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12958 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12959 * Used by Store query builder to append _requestMeta to params.
12962 metaFromRemote : false,
12964 * This method is only used by a DataProxy which has retrieved data from a remote server.
12965 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12966 * @return {Object} data A data block which is used by an Roo.data.Store object as
12967 * a cache of Roo.data.Records.
12969 read : function(response){
12970 var json = response.responseText;
12972 var o = /* eval:var:o */ eval("("+json+")");
12974 throw {message: "JsonReader.read: Json object not found"};
12980 this.metaFromRemote = true;
12981 this.meta = o.metaData;
12982 this.recordType = Roo.data.Record.create(o.metaData.fields);
12983 this.onMetaChange(this.meta, this.recordType, o);
12985 return this.readRecords(o);
12988 // private function a store will implement
12989 onMetaChange : function(meta, recordType, o){
12996 simpleAccess: function(obj, subsc) {
13003 getJsonAccessor: function(){
13005 return function(expr) {
13007 return(re.test(expr))
13008 ? new Function("obj", "return obj." + expr)
13013 return Roo.emptyFn;
13018 * Create a data block containing Roo.data.Records from an XML document.
13019 * @param {Object} o An object which contains an Array of row objects in the property specified
13020 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
13021 * which contains the total size of the dataset.
13022 * @return {Object} data A data block which is used by an Roo.data.Store object as
13023 * a cache of Roo.data.Records.
13025 readRecords : function(o){
13027 * After any data loads, the raw JSON data is available for further custom processing.
13031 var s = this.meta, Record = this.recordType,
13032 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
13034 // Generate extraction functions for the totalProperty, the root, the id, and for each field
13036 if(s.totalProperty) {
13037 this.getTotal = this.getJsonAccessor(s.totalProperty);
13039 if(s.successProperty) {
13040 this.getSuccess = this.getJsonAccessor(s.successProperty);
13042 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
13044 var g = this.getJsonAccessor(s.id);
13045 this.getId = function(rec) {
13047 return (r === undefined || r === "") ? null : r;
13050 this.getId = function(){return null;};
13053 for(var jj = 0; jj < fl; jj++){
13055 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
13056 this.ef[jj] = this.getJsonAccessor(map);
13060 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
13061 if(s.totalProperty){
13062 var vt = parseInt(this.getTotal(o), 10);
13067 if(s.successProperty){
13068 var vs = this.getSuccess(o);
13069 if(vs === false || vs === 'false'){
13074 for(var i = 0; i < c; i++){
13077 var id = this.getId(n);
13078 for(var j = 0; j < fl; j++){
13080 var v = this.ef[j](n);
13082 Roo.log('missing convert for ' + f.name);
13086 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13088 var record = new Record(values, id);
13090 records[i] = record;
13096 totalRecords : totalRecords
13101 * Ext JS Library 1.1.1
13102 * Copyright(c) 2006-2007, Ext JS, LLC.
13104 * Originally Released Under LGPL - original licence link has changed is not relivant.
13107 * <script type="text/javascript">
13111 * @class Roo.data.ArrayReader
13112 * @extends Roo.data.DataReader
13113 * Data reader class to create an Array of Roo.data.Record objects from an Array.
13114 * Each element of that Array represents a row of data fields. The
13115 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13116 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13120 var RecordDef = Roo.data.Record.create([
13121 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
13122 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
13124 var myReader = new Roo.data.ArrayReader({
13125 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
13129 * This would consume an Array like this:
13131 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13135 * Create a new JsonReader
13136 * @param {Object} meta Metadata configuration options.
13137 * @param {Object|Array} recordType Either an Array of field definition objects
13139 * @cfg {Array} fields Array of field definition objects
13140 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13141 * as specified to {@link Roo.data.Record#create},
13142 * or an {@link Roo.data.Record} object
13145 * created using {@link Roo.data.Record#create}.
13147 Roo.data.ArrayReader = function(meta, recordType){
13150 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13153 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13155 * Create a data block containing Roo.data.Records from an XML document.
13156 * @param {Object} o An Array of row objects which represents the dataset.
13157 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13158 * a cache of Roo.data.Records.
13160 readRecords : function(o){
13161 var sid = this.meta ? this.meta.id : null;
13162 var recordType = this.recordType, fields = recordType.prototype.fields;
13165 for(var i = 0; i < root.length; i++){
13168 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13169 for(var j = 0, jlen = fields.length; j < jlen; j++){
13170 var f = fields.items[j];
13171 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13172 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13174 values[f.name] = v;
13176 var record = new recordType(values, id);
13178 records[records.length] = record;
13182 totalRecords : records.length
13191 * @class Roo.bootstrap.ComboBox
13192 * @extends Roo.bootstrap.TriggerField
13193 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13194 * @cfg {Boolean} append (true|false) default false
13195 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13196 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13197 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13198 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13199 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13200 * @cfg {Boolean} animate default true
13201 * @cfg {Boolean} emptyResultText only for touch device
13202 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13203 * @cfg {String} emptyTitle default ''
13205 * Create a new ComboBox.
13206 * @param {Object} config Configuration options
13208 Roo.bootstrap.ComboBox = function(config){
13209 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13213 * Fires when the dropdown list is expanded
13214 * @param {Roo.bootstrap.ComboBox} combo This combo box
13219 * Fires when the dropdown list is collapsed
13220 * @param {Roo.bootstrap.ComboBox} combo This combo box
13224 * @event beforeselect
13225 * Fires before a list item is selected. Return false to cancel the selection.
13226 * @param {Roo.bootstrap.ComboBox} combo This combo box
13227 * @param {Roo.data.Record} record The data record returned from the underlying store
13228 * @param {Number} index The index of the selected item in the dropdown list
13230 'beforeselect' : true,
13233 * Fires when a list item is selected
13234 * @param {Roo.bootstrap.ComboBox} combo This combo box
13235 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13236 * @param {Number} index The index of the selected item in the dropdown list
13240 * @event beforequery
13241 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13242 * The event object passed has these properties:
13243 * @param {Roo.bootstrap.ComboBox} combo This combo box
13244 * @param {String} query The query
13245 * @param {Boolean} forceAll true to force "all" query
13246 * @param {Boolean} cancel true to cancel the query
13247 * @param {Object} e The query event object
13249 'beforequery': true,
13252 * Fires when the 'add' icon is pressed (add a listener to enable add button)
13253 * @param {Roo.bootstrap.ComboBox} combo This combo box
13258 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13259 * @param {Roo.bootstrap.ComboBox} combo This combo box
13260 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13265 * Fires when the remove value from the combobox array
13266 * @param {Roo.bootstrap.ComboBox} combo This combo box
13270 * @event afterremove
13271 * Fires when the remove value from the combobox array
13272 * @param {Roo.bootstrap.ComboBox} combo This combo box
13274 'afterremove' : true,
13276 * @event specialfilter
13277 * Fires when specialfilter
13278 * @param {Roo.bootstrap.ComboBox} combo This combo box
13280 'specialfilter' : true,
13283 * Fires when tick the element
13284 * @param {Roo.bootstrap.ComboBox} combo This combo box
13288 * @event touchviewdisplay
13289 * Fires when touch view require special display (default is using displayField)
13290 * @param {Roo.bootstrap.ComboBox} combo This combo box
13291 * @param {Object} cfg set html .
13293 'touchviewdisplay' : true
13298 this.tickItems = [];
13300 this.selectedIndex = -1;
13301 if(this.mode == 'local'){
13302 if(config.queryDelay === undefined){
13303 this.queryDelay = 10;
13305 if(config.minChars === undefined){
13311 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13314 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13315 * rendering into an Roo.Editor, defaults to false)
13318 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13319 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13322 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13325 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13326 * the dropdown list (defaults to undefined, with no header element)
13330 * @cfg {String/Roo.Template} tpl The template to use to render the output
13334 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13336 listWidth: undefined,
13338 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13339 * mode = 'remote' or 'text' if mode = 'local')
13341 displayField: undefined,
13344 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13345 * mode = 'remote' or 'value' if mode = 'local').
13346 * Note: use of a valueField requires the user make a selection
13347 * in order for a value to be mapped.
13349 valueField: undefined,
13351 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13356 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13357 * field's data value (defaults to the underlying DOM element's name)
13359 hiddenName: undefined,
13361 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13365 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13367 selectedClass: 'active',
13370 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13374 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13375 * anchor positions (defaults to 'tl-bl')
13377 listAlign: 'tl-bl?',
13379 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13383 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13384 * query specified by the allQuery config option (defaults to 'query')
13386 triggerAction: 'query',
13388 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13389 * (defaults to 4, does not apply if editable = false)
13393 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13394 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13398 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13399 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13403 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13404 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13408 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13409 * when editable = true (defaults to false)
13411 selectOnFocus:false,
13413 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13415 queryParam: 'query',
13417 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13418 * when mode = 'remote' (defaults to 'Loading...')
13420 loadingText: 'Loading...',
13422 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13426 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13430 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13431 * traditional select (defaults to true)
13435 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13439 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13443 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13444 * listWidth has a higher value)
13448 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13449 * allow the user to set arbitrary text into the field (defaults to false)
13451 forceSelection:false,
13453 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13454 * if typeAhead = true (defaults to 250)
13456 typeAheadDelay : 250,
13458 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13459 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13461 valueNotFoundText : undefined,
13463 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13465 blockFocus : false,
13468 * @cfg {Boolean} disableClear Disable showing of clear button.
13470 disableClear : false,
13472 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13474 alwaysQuery : false,
13477 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13482 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13484 invalidClass : "has-warning",
13487 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13489 validClass : "has-success",
13492 * @cfg {Boolean} specialFilter (true|false) special filter default false
13494 specialFilter : false,
13497 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13499 mobileTouchView : true,
13502 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13504 useNativeIOS : false,
13507 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13509 mobile_restrict_height : false,
13511 ios_options : false,
13523 btnPosition : 'right',
13524 triggerList : true,
13525 showToggleBtn : true,
13527 emptyResultText: 'Empty',
13528 triggerText : 'Select',
13531 // element that contains real text value.. (when hidden is used..)
13533 getAutoCreate : function()
13538 * Render classic select for iso
13541 if(Roo.isIOS && this.useNativeIOS){
13542 cfg = this.getAutoCreateNativeIOS();
13550 if(Roo.isTouch && this.mobileTouchView){
13551 cfg = this.getAutoCreateTouchView();
13558 if(!this.tickable){
13559 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13564 * ComboBox with tickable selections
13567 var align = this.labelAlign || this.parentLabelAlign();
13570 cls : 'form-group roo-combobox-tickable' //input-group
13573 var btn_text_select = '';
13574 var btn_text_done = '';
13575 var btn_text_cancel = '';
13577 if (this.btn_text_show) {
13578 btn_text_select = 'Select';
13579 btn_text_done = 'Done';
13580 btn_text_cancel = 'Cancel';
13585 cls : 'tickable-buttons',
13590 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13591 //html : this.triggerText
13592 html: btn_text_select
13598 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13600 html: btn_text_done
13606 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13608 html: btn_text_cancel
13614 buttons.cn.unshift({
13616 cls: 'roo-select2-search-field-input'
13622 Roo.each(buttons.cn, function(c){
13624 c.cls += ' btn-' + _this.size;
13627 if (_this.disabled) {
13634 style : 'display: contents',
13639 cls: 'form-hidden-field'
13643 cls: 'roo-select2-choices',
13647 cls: 'roo-select2-search-field',
13658 cls: 'roo-select2-container input-group roo-select2-container-multi',
13664 // cls: 'typeahead typeahead-long dropdown-menu',
13665 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13670 if(this.hasFeedback && !this.allowBlank){
13674 cls: 'glyphicon form-control-feedback'
13677 combobox.cn.push(feedback);
13682 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13683 tooltip : 'This field is required'
13685 if (Roo.bootstrap.version == 4) {
13688 style : 'display:none'
13691 if (align ==='left' && this.fieldLabel.length) {
13693 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13700 cls : 'control-label col-form-label',
13701 html : this.fieldLabel
13713 var labelCfg = cfg.cn[1];
13714 var contentCfg = cfg.cn[2];
13717 if(this.indicatorpos == 'right'){
13723 cls : 'control-label col-form-label',
13727 html : this.fieldLabel
13743 labelCfg = cfg.cn[0];
13744 contentCfg = cfg.cn[1];
13748 if(this.labelWidth > 12){
13749 labelCfg.style = "width: " + this.labelWidth + 'px';
13752 if(this.labelWidth < 13 && this.labelmd == 0){
13753 this.labelmd = this.labelWidth;
13756 if(this.labellg > 0){
13757 labelCfg.cls += ' col-lg-' + this.labellg;
13758 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13761 if(this.labelmd > 0){
13762 labelCfg.cls += ' col-md-' + this.labelmd;
13763 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13766 if(this.labelsm > 0){
13767 labelCfg.cls += ' col-sm-' + this.labelsm;
13768 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13771 if(this.labelxs > 0){
13772 labelCfg.cls += ' col-xs-' + this.labelxs;
13773 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13777 } else if ( this.fieldLabel.length) {
13778 // Roo.log(" label");
13783 //cls : 'input-group-addon',
13784 html : this.fieldLabel
13789 if(this.indicatorpos == 'right'){
13793 //cls : 'input-group-addon',
13794 html : this.fieldLabel
13804 // Roo.log(" no label && no align");
13811 ['xs','sm','md','lg'].map(function(size){
13812 if (settings[size]) {
13813 cfg.cls += ' col-' + size + '-' + settings[size];
13821 _initEventsCalled : false,
13824 initEvents: function()
13826 if (this._initEventsCalled) { // as we call render... prevent looping...
13829 this._initEventsCalled = true;
13832 throw "can not find store for combo";
13835 this.indicator = this.indicatorEl();
13837 this.store = Roo.factory(this.store, Roo.data);
13838 this.store.parent = this;
13840 // if we are building from html. then this element is so complex, that we can not really
13841 // use the rendered HTML.
13842 // so we have to trash and replace the previous code.
13843 if (Roo.XComponent.build_from_html) {
13844 // remove this element....
13845 var e = this.el.dom, k=0;
13846 while (e ) { e = e.previousSibling; ++k;}
13851 this.rendered = false;
13853 this.render(this.parent().getChildContainer(true), k);
13856 if(Roo.isIOS && this.useNativeIOS){
13857 this.initIOSView();
13865 if(Roo.isTouch && this.mobileTouchView){
13866 this.initTouchView();
13871 this.initTickableEvents();
13875 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13877 if(this.hiddenName){
13879 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13881 this.hiddenField.dom.value =
13882 this.hiddenValue !== undefined ? this.hiddenValue :
13883 this.value !== undefined ? this.value : '';
13885 // prevent input submission
13886 this.el.dom.removeAttribute('name');
13887 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13892 // this.el.dom.setAttribute('autocomplete', 'off');
13895 var cls = 'x-combo-list';
13897 //this.list = new Roo.Layer({
13898 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13904 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13905 _this.list.setWidth(lw);
13908 this.list.on('mouseover', this.onViewOver, this);
13909 this.list.on('mousemove', this.onViewMove, this);
13910 this.list.on('scroll', this.onViewScroll, this);
13913 this.list.swallowEvent('mousewheel');
13914 this.assetHeight = 0;
13917 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13918 this.assetHeight += this.header.getHeight();
13921 this.innerList = this.list.createChild({cls:cls+'-inner'});
13922 this.innerList.on('mouseover', this.onViewOver, this);
13923 this.innerList.on('mousemove', this.onViewMove, this);
13924 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13926 if(this.allowBlank && !this.pageSize && !this.disableClear){
13927 this.footer = this.list.createChild({cls:cls+'-ft'});
13928 this.pageTb = new Roo.Toolbar(this.footer);
13932 this.footer = this.list.createChild({cls:cls+'-ft'});
13933 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13934 {pageSize: this.pageSize});
13938 if (this.pageTb && this.allowBlank && !this.disableClear) {
13940 this.pageTb.add(new Roo.Toolbar.Fill(), {
13941 cls: 'x-btn-icon x-btn-clear',
13943 handler: function()
13946 _this.clearValue();
13947 _this.onSelect(false, -1);
13952 this.assetHeight += this.footer.getHeight();
13957 this.tpl = Roo.bootstrap.version == 4 ?
13958 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
13959 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13962 this.view = new Roo.View(this.list, this.tpl, {
13963 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13965 //this.view.wrapEl.setDisplayed(false);
13966 this.view.on('click', this.onViewClick, this);
13969 this.store.on('beforeload', this.onBeforeLoad, this);
13970 this.store.on('load', this.onLoad, this);
13971 this.store.on('loadexception', this.onLoadException, this);
13973 if(this.resizable){
13974 this.resizer = new Roo.Resizable(this.list, {
13975 pinned:true, handles:'se'
13977 this.resizer.on('resize', function(r, w, h){
13978 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13979 this.listWidth = w;
13980 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13981 this.restrictHeight();
13983 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13986 if(!this.editable){
13987 this.editable = true;
13988 this.setEditable(false);
13993 if (typeof(this.events.add.listeners) != 'undefined') {
13995 this.addicon = this.wrap.createChild(
13996 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13998 this.addicon.on('click', function(e) {
13999 this.fireEvent('add', this);
14002 if (typeof(this.events.edit.listeners) != 'undefined') {
14004 this.editicon = this.wrap.createChild(
14005 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
14006 if (this.addicon) {
14007 this.editicon.setStyle('margin-left', '40px');
14009 this.editicon.on('click', function(e) {
14011 // we fire even if inothing is selected..
14012 this.fireEvent('edit', this, this.lastData );
14018 this.keyNav = new Roo.KeyNav(this.inputEl(), {
14019 "up" : function(e){
14020 this.inKeyMode = true;
14024 "down" : function(e){
14025 if(!this.isExpanded()){
14026 this.onTriggerClick();
14028 this.inKeyMode = true;
14033 "enter" : function(e){
14034 // this.onViewClick();
14038 if(this.fireEvent("specialkey", this, e)){
14039 this.onViewClick(false);
14045 "esc" : function(e){
14049 "tab" : function(e){
14052 if(this.fireEvent("specialkey", this, e)){
14053 this.onViewClick(false);
14061 doRelay : function(foo, bar, hname){
14062 if(hname == 'down' || this.scope.isExpanded()){
14063 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14072 this.queryDelay = Math.max(this.queryDelay || 10,
14073 this.mode == 'local' ? 10 : 250);
14076 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14078 if(this.typeAhead){
14079 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14081 if(this.editable !== false){
14082 this.inputEl().on("keyup", this.onKeyUp, this);
14084 if(this.forceSelection){
14085 this.inputEl().on('blur', this.doForce, this);
14089 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14090 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14094 initTickableEvents: function()
14098 if(this.hiddenName){
14100 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14102 this.hiddenField.dom.value =
14103 this.hiddenValue !== undefined ? this.hiddenValue :
14104 this.value !== undefined ? this.value : '';
14106 // prevent input submission
14107 this.el.dom.removeAttribute('name');
14108 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14113 // this.list = this.el.select('ul.dropdown-menu',true).first();
14115 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14116 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14117 if(this.triggerList){
14118 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14121 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14122 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14124 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14125 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14127 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14128 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14130 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14131 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14132 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14135 this.cancelBtn.hide();
14140 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14141 _this.list.setWidth(lw);
14144 this.list.on('mouseover', this.onViewOver, this);
14145 this.list.on('mousemove', this.onViewMove, this);
14147 this.list.on('scroll', this.onViewScroll, this);
14150 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
14151 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14154 this.view = new Roo.View(this.list, this.tpl, {
14159 selectedClass: this.selectedClass
14162 //this.view.wrapEl.setDisplayed(false);
14163 this.view.on('click', this.onViewClick, this);
14167 this.store.on('beforeload', this.onBeforeLoad, this);
14168 this.store.on('load', this.onLoad, this);
14169 this.store.on('loadexception', this.onLoadException, this);
14172 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14173 "up" : function(e){
14174 this.inKeyMode = true;
14178 "down" : function(e){
14179 this.inKeyMode = true;
14183 "enter" : function(e){
14184 if(this.fireEvent("specialkey", this, e)){
14185 this.onViewClick(false);
14191 "esc" : function(e){
14192 this.onTickableFooterButtonClick(e, false, false);
14195 "tab" : function(e){
14196 this.fireEvent("specialkey", this, e);
14198 this.onTickableFooterButtonClick(e, false, false);
14205 doRelay : function(e, fn, key){
14206 if(this.scope.isExpanded()){
14207 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14216 this.queryDelay = Math.max(this.queryDelay || 10,
14217 this.mode == 'local' ? 10 : 250);
14220 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14222 if(this.typeAhead){
14223 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14226 if(this.editable !== false){
14227 this.tickableInputEl().on("keyup", this.onKeyUp, this);
14230 this.indicator = this.indicatorEl();
14232 if(this.indicator){
14233 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14234 this.indicator.hide();
14239 onDestroy : function(){
14241 this.view.setStore(null);
14242 this.view.el.removeAllListeners();
14243 this.view.el.remove();
14244 this.view.purgeListeners();
14247 this.list.dom.innerHTML = '';
14251 this.store.un('beforeload', this.onBeforeLoad, this);
14252 this.store.un('load', this.onLoad, this);
14253 this.store.un('loadexception', this.onLoadException, this);
14255 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14259 fireKey : function(e){
14260 if(e.isNavKeyPress() && !this.list.isVisible()){
14261 this.fireEvent("specialkey", this, e);
14266 onResize: function(w, h){
14267 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14269 // if(typeof w != 'number'){
14270 // // we do not handle it!?!?
14273 // var tw = this.trigger.getWidth();
14274 // // tw += this.addicon ? this.addicon.getWidth() : 0;
14275 // // tw += this.editicon ? this.editicon.getWidth() : 0;
14277 // this.inputEl().setWidth( this.adjustWidth('input', x));
14279 // //this.trigger.setStyle('left', x+'px');
14281 // if(this.list && this.listWidth === undefined){
14282 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14283 // this.list.setWidth(lw);
14284 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14292 * Allow or prevent the user from directly editing the field text. If false is passed,
14293 * the user will only be able to select from the items defined in the dropdown list. This method
14294 * is the runtime equivalent of setting the 'editable' config option at config time.
14295 * @param {Boolean} value True to allow the user to directly edit the field text
14297 setEditable : function(value){
14298 if(value == this.editable){
14301 this.editable = value;
14303 this.inputEl().dom.setAttribute('readOnly', true);
14304 this.inputEl().on('mousedown', this.onTriggerClick, this);
14305 this.inputEl().addClass('x-combo-noedit');
14307 this.inputEl().dom.setAttribute('readOnly', false);
14308 this.inputEl().un('mousedown', this.onTriggerClick, this);
14309 this.inputEl().removeClass('x-combo-noedit');
14315 onBeforeLoad : function(combo,opts){
14316 if(!this.hasFocus){
14320 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14322 this.restrictHeight();
14323 this.selectedIndex = -1;
14327 onLoad : function(){
14329 this.hasQuery = false;
14331 if(!this.hasFocus){
14335 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14336 this.loading.hide();
14339 if(this.store.getCount() > 0){
14342 this.restrictHeight();
14343 if(this.lastQuery == this.allQuery){
14344 if(this.editable && !this.tickable){
14345 this.inputEl().dom.select();
14349 !this.selectByValue(this.value, true) &&
14352 !this.store.lastOptions ||
14353 typeof(this.store.lastOptions.add) == 'undefined' ||
14354 this.store.lastOptions.add != true
14357 this.select(0, true);
14360 if(this.autoFocus){
14363 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14364 this.taTask.delay(this.typeAheadDelay);
14368 this.onEmptyResults();
14374 onLoadException : function()
14376 this.hasQuery = false;
14378 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14379 this.loading.hide();
14382 if(this.tickable && this.editable){
14387 // only causes errors at present
14388 //Roo.log(this.store.reader.jsonData);
14389 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14391 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14397 onTypeAhead : function(){
14398 if(this.store.getCount() > 0){
14399 var r = this.store.getAt(0);
14400 var newValue = r.data[this.displayField];
14401 var len = newValue.length;
14402 var selStart = this.getRawValue().length;
14404 if(selStart != len){
14405 this.setRawValue(newValue);
14406 this.selectText(selStart, newValue.length);
14412 onSelect : function(record, index){
14414 if(this.fireEvent('beforeselect', this, record, index) !== false){
14416 this.setFromData(index > -1 ? record.data : false);
14419 this.fireEvent('select', this, record, index);
14424 * Returns the currently selected field value or empty string if no value is set.
14425 * @return {String} value The selected value
14427 getValue : function()
14429 if(Roo.isIOS && this.useNativeIOS){
14430 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14434 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14437 if(this.valueField){
14438 return typeof this.value != 'undefined' ? this.value : '';
14440 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14444 getRawValue : function()
14446 if(Roo.isIOS && this.useNativeIOS){
14447 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14450 var v = this.inputEl().getValue();
14456 * Clears any text/value currently set in the field
14458 clearValue : function(){
14460 if(this.hiddenField){
14461 this.hiddenField.dom.value = '';
14464 this.setRawValue('');
14465 this.lastSelectionText = '';
14466 this.lastData = false;
14468 var close = this.closeTriggerEl();
14479 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14480 * will be displayed in the field. If the value does not match the data value of an existing item,
14481 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14482 * Otherwise the field will be blank (although the value will still be set).
14483 * @param {String} value The value to match
14485 setValue : function(v)
14487 if(Roo.isIOS && this.useNativeIOS){
14488 this.setIOSValue(v);
14498 if(this.valueField){
14499 var r = this.findRecord(this.valueField, v);
14501 text = r.data[this.displayField];
14502 }else if(this.valueNotFoundText !== undefined){
14503 text = this.valueNotFoundText;
14506 this.lastSelectionText = text;
14507 if(this.hiddenField){
14508 this.hiddenField.dom.value = v;
14510 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14513 var close = this.closeTriggerEl();
14516 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14522 * @property {Object} the last set data for the element
14527 * Sets the value of the field based on a object which is related to the record format for the store.
14528 * @param {Object} value the value to set as. or false on reset?
14530 setFromData : function(o){
14537 var dv = ''; // display value
14538 var vv = ''; // value value..
14540 if (this.displayField) {
14541 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14543 // this is an error condition!!!
14544 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14547 if(this.valueField){
14548 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14551 var close = this.closeTriggerEl();
14554 if(dv.length || vv * 1 > 0){
14556 this.blockFocus=true;
14562 if(this.hiddenField){
14563 this.hiddenField.dom.value = vv;
14565 this.lastSelectionText = dv;
14566 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14570 // no hidden field.. - we store the value in 'value', but still display
14571 // display field!!!!
14572 this.lastSelectionText = dv;
14573 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14580 reset : function(){
14581 // overridden so that last data is reset..
14588 this.setValue(this.originalValue);
14589 //this.clearInvalid();
14590 this.lastData = false;
14592 this.view.clearSelections();
14598 findRecord : function(prop, value){
14600 if(this.store.getCount() > 0){
14601 this.store.each(function(r){
14602 if(r.data[prop] == value){
14612 getName: function()
14614 // returns hidden if it's set..
14615 if (!this.rendered) {return ''};
14616 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14620 onViewMove : function(e, t){
14621 this.inKeyMode = false;
14625 onViewOver : function(e, t){
14626 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14629 var item = this.view.findItemFromChild(t);
14632 var index = this.view.indexOf(item);
14633 this.select(index, false);
14638 onViewClick : function(view, doFocus, el, e)
14640 var index = this.view.getSelectedIndexes()[0];
14642 var r = this.store.getAt(index);
14646 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14653 Roo.each(this.tickItems, function(v,k){
14655 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14657 _this.tickItems.splice(k, 1);
14659 if(typeof(e) == 'undefined' && view == false){
14660 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14672 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14673 this.tickItems.push(r.data);
14676 if(typeof(e) == 'undefined' && view == false){
14677 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14684 this.onSelect(r, index);
14686 if(doFocus !== false && !this.blockFocus){
14687 this.inputEl().focus();
14692 restrictHeight : function(){
14693 //this.innerList.dom.style.height = '';
14694 //var inner = this.innerList.dom;
14695 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14696 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14697 //this.list.beginUpdate();
14698 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14699 this.list.alignTo(this.inputEl(), this.listAlign);
14700 this.list.alignTo(this.inputEl(), this.listAlign);
14701 //this.list.endUpdate();
14705 onEmptyResults : function(){
14707 if(this.tickable && this.editable){
14708 this.hasFocus = false;
14709 this.restrictHeight();
14717 * Returns true if the dropdown list is expanded, else false.
14719 isExpanded : function(){
14720 return this.list.isVisible();
14724 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14725 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14726 * @param {String} value The data value of the item to select
14727 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14728 * selected item if it is not currently in view (defaults to true)
14729 * @return {Boolean} True if the value matched an item in the list, else false
14731 selectByValue : function(v, scrollIntoView){
14732 if(v !== undefined && v !== null){
14733 var r = this.findRecord(this.valueField || this.displayField, v);
14735 this.select(this.store.indexOf(r), scrollIntoView);
14743 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14744 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14745 * @param {Number} index The zero-based index of the list item to select
14746 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14747 * selected item if it is not currently in view (defaults to true)
14749 select : function(index, scrollIntoView){
14750 this.selectedIndex = index;
14751 this.view.select(index);
14752 if(scrollIntoView !== false){
14753 var el = this.view.getNode(index);
14755 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14758 this.list.scrollChildIntoView(el, false);
14764 selectNext : function(){
14765 var ct = this.store.getCount();
14767 if(this.selectedIndex == -1){
14769 }else if(this.selectedIndex < ct-1){
14770 this.select(this.selectedIndex+1);
14776 selectPrev : function(){
14777 var ct = this.store.getCount();
14779 if(this.selectedIndex == -1){
14781 }else if(this.selectedIndex != 0){
14782 this.select(this.selectedIndex-1);
14788 onKeyUp : function(e){
14789 if(this.editable !== false && !e.isSpecialKey()){
14790 this.lastKey = e.getKey();
14791 this.dqTask.delay(this.queryDelay);
14796 validateBlur : function(){
14797 return !this.list || !this.list.isVisible();
14801 initQuery : function(){
14803 var v = this.getRawValue();
14805 if(this.tickable && this.editable){
14806 v = this.tickableInputEl().getValue();
14813 doForce : function(){
14814 if(this.inputEl().dom.value.length > 0){
14815 this.inputEl().dom.value =
14816 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14822 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14823 * query allowing the query action to be canceled if needed.
14824 * @param {String} query The SQL query to execute
14825 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14826 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14827 * saved in the current store (defaults to false)
14829 doQuery : function(q, forceAll){
14831 if(q === undefined || q === null){
14836 forceAll: forceAll,
14840 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14845 forceAll = qe.forceAll;
14846 if(forceAll === true || (q.length >= this.minChars)){
14848 this.hasQuery = true;
14850 if(this.lastQuery != q || this.alwaysQuery){
14851 this.lastQuery = q;
14852 if(this.mode == 'local'){
14853 this.selectedIndex = -1;
14855 this.store.clearFilter();
14858 if(this.specialFilter){
14859 this.fireEvent('specialfilter', this);
14864 this.store.filter(this.displayField, q);
14867 this.store.fireEvent("datachanged", this.store);
14874 this.store.baseParams[this.queryParam] = q;
14876 var options = {params : this.getParams(q)};
14879 options.add = true;
14880 options.params.start = this.page * this.pageSize;
14883 this.store.load(options);
14886 * this code will make the page width larger, at the beginning, the list not align correctly,
14887 * we should expand the list on onLoad
14888 * so command out it
14893 this.selectedIndex = -1;
14898 this.loadNext = false;
14902 getParams : function(q){
14904 //p[this.queryParam] = q;
14908 p.limit = this.pageSize;
14914 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14916 collapse : function(){
14917 if(!this.isExpanded()){
14923 this.hasFocus = false;
14927 this.cancelBtn.hide();
14928 this.trigger.show();
14931 this.tickableInputEl().dom.value = '';
14932 this.tickableInputEl().blur();
14937 Roo.get(document).un('mousedown', this.collapseIf, this);
14938 Roo.get(document).un('mousewheel', this.collapseIf, this);
14939 if (!this.editable) {
14940 Roo.get(document).un('keydown', this.listKeyPress, this);
14942 this.fireEvent('collapse', this);
14948 collapseIf : function(e){
14949 var in_combo = e.within(this.el);
14950 var in_list = e.within(this.list);
14951 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14953 if (in_combo || in_list || is_list) {
14954 //e.stopPropagation();
14959 this.onTickableFooterButtonClick(e, false, false);
14967 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14969 expand : function(){
14971 if(this.isExpanded() || !this.hasFocus){
14975 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14976 this.list.setWidth(lw);
14982 this.restrictHeight();
14986 this.tickItems = Roo.apply([], this.item);
14989 this.cancelBtn.show();
14990 this.trigger.hide();
14993 this.tickableInputEl().focus();
14998 Roo.get(document).on('mousedown', this.collapseIf, this);
14999 Roo.get(document).on('mousewheel', this.collapseIf, this);
15000 if (!this.editable) {
15001 Roo.get(document).on('keydown', this.listKeyPress, this);
15004 this.fireEvent('expand', this);
15008 // Implements the default empty TriggerField.onTriggerClick function
15009 onTriggerClick : function(e)
15011 Roo.log('trigger click');
15013 if(this.disabled || !this.triggerList){
15018 this.loadNext = false;
15020 if(this.isExpanded()){
15022 if (!this.blockFocus) {
15023 this.inputEl().focus();
15027 this.hasFocus = true;
15028 if(this.triggerAction == 'all') {
15029 this.doQuery(this.allQuery, true);
15031 this.doQuery(this.getRawValue());
15033 if (!this.blockFocus) {
15034 this.inputEl().focus();
15039 onTickableTriggerClick : function(e)
15046 this.loadNext = false;
15047 this.hasFocus = true;
15049 if(this.triggerAction == 'all') {
15050 this.doQuery(this.allQuery, true);
15052 this.doQuery(this.getRawValue());
15056 onSearchFieldClick : function(e)
15058 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15059 this.onTickableFooterButtonClick(e, false, false);
15063 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15068 this.loadNext = false;
15069 this.hasFocus = true;
15071 if(this.triggerAction == 'all') {
15072 this.doQuery(this.allQuery, true);
15074 this.doQuery(this.getRawValue());
15078 listKeyPress : function(e)
15080 //Roo.log('listkeypress');
15081 // scroll to first matching element based on key pres..
15082 if (e.isSpecialKey()) {
15085 var k = String.fromCharCode(e.getKey()).toUpperCase();
15088 var csel = this.view.getSelectedNodes();
15089 var cselitem = false;
15091 var ix = this.view.indexOf(csel[0]);
15092 cselitem = this.store.getAt(ix);
15093 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15099 this.store.each(function(v) {
15101 // start at existing selection.
15102 if (cselitem.id == v.id) {
15108 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15109 match = this.store.indexOf(v);
15115 if (match === false) {
15116 return true; // no more action?
15119 this.view.select(match);
15120 var sn = Roo.get(this.view.getSelectedNodes()[0]);
15121 sn.scrollIntoView(sn.dom.parentNode, false);
15124 onViewScroll : function(e, t){
15126 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){
15130 this.hasQuery = true;
15132 this.loading = this.list.select('.loading', true).first();
15134 if(this.loading === null){
15135 this.list.createChild({
15137 cls: 'loading roo-select2-more-results roo-select2-active',
15138 html: 'Loading more results...'
15141 this.loading = this.list.select('.loading', true).first();
15143 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15145 this.loading.hide();
15148 this.loading.show();
15153 this.loadNext = true;
15155 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15160 addItem : function(o)
15162 var dv = ''; // display value
15164 if (this.displayField) {
15165 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15167 // this is an error condition!!!
15168 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
15175 var choice = this.choices.createChild({
15177 cls: 'roo-select2-search-choice',
15186 cls: 'roo-select2-search-choice-close fa fa-times',
15191 }, this.searchField);
15193 var close = choice.select('a.roo-select2-search-choice-close', true).first();
15195 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15203 this.inputEl().dom.value = '';
15208 onRemoveItem : function(e, _self, o)
15210 e.preventDefault();
15212 this.lastItem = Roo.apply([], this.item);
15214 var index = this.item.indexOf(o.data) * 1;
15217 Roo.log('not this item?!');
15221 this.item.splice(index, 1);
15226 this.fireEvent('remove', this, e);
15232 syncValue : function()
15234 if(!this.item.length){
15241 Roo.each(this.item, function(i){
15242 if(_this.valueField){
15243 value.push(i[_this.valueField]);
15250 this.value = value.join(',');
15252 if(this.hiddenField){
15253 this.hiddenField.dom.value = this.value;
15256 this.store.fireEvent("datachanged", this.store);
15261 clearItem : function()
15263 if(!this.multiple){
15269 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15277 if(this.tickable && !Roo.isTouch){
15278 this.view.refresh();
15282 inputEl: function ()
15284 if(Roo.isIOS && this.useNativeIOS){
15285 return this.el.select('select.roo-ios-select', true).first();
15288 if(Roo.isTouch && this.mobileTouchView){
15289 return this.el.select('input.form-control',true).first();
15293 return this.searchField;
15296 return this.el.select('input.form-control',true).first();
15299 onTickableFooterButtonClick : function(e, btn, el)
15301 e.preventDefault();
15303 this.lastItem = Roo.apply([], this.item);
15305 if(btn && btn.name == 'cancel'){
15306 this.tickItems = Roo.apply([], this.item);
15315 Roo.each(this.tickItems, function(o){
15323 validate : function()
15325 if(this.getVisibilityEl().hasClass('hidden')){
15329 var v = this.getRawValue();
15332 v = this.getValue();
15335 if(this.disabled || this.allowBlank || v.length){
15340 this.markInvalid();
15344 tickableInputEl : function()
15346 if(!this.tickable || !this.editable){
15347 return this.inputEl();
15350 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15354 getAutoCreateTouchView : function()
15359 cls: 'form-group' //input-group
15365 type : this.inputType,
15366 cls : 'form-control x-combo-noedit',
15367 autocomplete: 'new-password',
15368 placeholder : this.placeholder || '',
15373 input.name = this.name;
15377 input.cls += ' input-' + this.size;
15380 if (this.disabled) {
15381 input.disabled = true;
15392 inputblock.cls += ' input-group';
15394 inputblock.cn.unshift({
15396 cls : 'input-group-addon input-group-prepend input-group-text',
15401 if(this.removable && !this.multiple){
15402 inputblock.cls += ' roo-removable';
15404 inputblock.cn.push({
15407 cls : 'roo-combo-removable-btn close'
15411 if(this.hasFeedback && !this.allowBlank){
15413 inputblock.cls += ' has-feedback';
15415 inputblock.cn.push({
15417 cls: 'glyphicon form-control-feedback'
15424 inputblock.cls += (this.before) ? '' : ' input-group';
15426 inputblock.cn.push({
15428 cls : 'input-group-addon input-group-append input-group-text',
15434 var ibwrap = inputblock;
15439 cls: 'roo-select2-choices',
15443 cls: 'roo-select2-search-field',
15456 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15461 cls: 'form-hidden-field'
15467 if(!this.multiple && this.showToggleBtn){
15473 if (this.caret != false) {
15476 cls: 'fa fa-' + this.caret
15483 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15485 Roo.bootstrap.version == 3 ? caret : '',
15488 cls: 'combobox-clear',
15502 combobox.cls += ' roo-select2-container-multi';
15505 var align = this.labelAlign || this.parentLabelAlign();
15507 if (align ==='left' && this.fieldLabel.length) {
15512 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15513 tooltip : 'This field is required'
15517 cls : 'control-label col-form-label',
15518 html : this.fieldLabel
15529 var labelCfg = cfg.cn[1];
15530 var contentCfg = cfg.cn[2];
15533 if(this.indicatorpos == 'right'){
15538 cls : 'control-label col-form-label',
15542 html : this.fieldLabel
15546 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15547 tooltip : 'This field is required'
15560 labelCfg = cfg.cn[0];
15561 contentCfg = cfg.cn[1];
15566 if(this.labelWidth > 12){
15567 labelCfg.style = "width: " + this.labelWidth + 'px';
15570 if(this.labelWidth < 13 && this.labelmd == 0){
15571 this.labelmd = this.labelWidth;
15574 if(this.labellg > 0){
15575 labelCfg.cls += ' col-lg-' + this.labellg;
15576 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15579 if(this.labelmd > 0){
15580 labelCfg.cls += ' col-md-' + this.labelmd;
15581 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15584 if(this.labelsm > 0){
15585 labelCfg.cls += ' col-sm-' + this.labelsm;
15586 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15589 if(this.labelxs > 0){
15590 labelCfg.cls += ' col-xs-' + this.labelxs;
15591 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15595 } else if ( this.fieldLabel.length) {
15599 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15600 tooltip : 'This field is required'
15604 cls : 'control-label',
15605 html : this.fieldLabel
15616 if(this.indicatorpos == 'right'){
15620 cls : 'control-label',
15621 html : this.fieldLabel,
15625 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15626 tooltip : 'This field is required'
15643 var settings = this;
15645 ['xs','sm','md','lg'].map(function(size){
15646 if (settings[size]) {
15647 cfg.cls += ' col-' + size + '-' + settings[size];
15654 initTouchView : function()
15656 this.renderTouchView();
15658 this.touchViewEl.on('scroll', function(){
15659 this.el.dom.scrollTop = 0;
15662 this.originalValue = this.getValue();
15664 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15666 this.inputEl().on("click", this.showTouchView, this);
15667 if (this.triggerEl) {
15668 this.triggerEl.on("click", this.showTouchView, this);
15672 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15673 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15675 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15677 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15678 this.store.on('load', this.onTouchViewLoad, this);
15679 this.store.on('loadexception', this.onTouchViewLoadException, this);
15681 if(this.hiddenName){
15683 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15685 this.hiddenField.dom.value =
15686 this.hiddenValue !== undefined ? this.hiddenValue :
15687 this.value !== undefined ? this.value : '';
15689 this.el.dom.removeAttribute('name');
15690 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15694 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15695 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15698 if(this.removable && !this.multiple){
15699 var close = this.closeTriggerEl();
15701 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15702 close.on('click', this.removeBtnClick, this, close);
15706 * fix the bug in Safari iOS8
15708 this.inputEl().on("focus", function(e){
15709 document.activeElement.blur();
15712 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15719 renderTouchView : function()
15721 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15722 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15724 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15725 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15727 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15728 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15729 this.touchViewBodyEl.setStyle('overflow', 'auto');
15731 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15732 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15734 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15735 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15739 showTouchView : function()
15745 this.touchViewHeaderEl.hide();
15747 if(this.modalTitle.length){
15748 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15749 this.touchViewHeaderEl.show();
15752 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15753 this.touchViewEl.show();
15755 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15757 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15758 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15760 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15762 if(this.modalTitle.length){
15763 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15766 this.touchViewBodyEl.setHeight(bodyHeight);
15770 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15772 this.touchViewEl.addClass('in');
15775 if(this._touchViewMask){
15776 Roo.get(document.body).addClass("x-body-masked");
15777 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15778 this._touchViewMask.setStyle('z-index', 10000);
15779 this._touchViewMask.addClass('show');
15782 this.doTouchViewQuery();
15786 hideTouchView : function()
15788 this.touchViewEl.removeClass('in');
15792 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15794 this.touchViewEl.setStyle('display', 'none');
15797 if(this._touchViewMask){
15798 this._touchViewMask.removeClass('show');
15799 Roo.get(document.body).removeClass("x-body-masked");
15803 setTouchViewValue : function()
15810 Roo.each(this.tickItems, function(o){
15815 this.hideTouchView();
15818 doTouchViewQuery : function()
15827 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15831 if(!this.alwaysQuery || this.mode == 'local'){
15832 this.onTouchViewLoad();
15839 onTouchViewBeforeLoad : function(combo,opts)
15845 onTouchViewLoad : function()
15847 if(this.store.getCount() < 1){
15848 this.onTouchViewEmptyResults();
15852 this.clearTouchView();
15854 var rawValue = this.getRawValue();
15856 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15858 this.tickItems = [];
15860 this.store.data.each(function(d, rowIndex){
15861 var row = this.touchViewListGroup.createChild(template);
15863 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15864 row.addClass(d.data.cls);
15867 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15870 html : d.data[this.displayField]
15873 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15874 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15877 row.removeClass('selected');
15878 if(!this.multiple && this.valueField &&
15879 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15882 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15883 row.addClass('selected');
15886 if(this.multiple && this.valueField &&
15887 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15891 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15892 this.tickItems.push(d.data);
15895 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15899 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15901 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15903 if(this.modalTitle.length){
15904 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15907 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15909 if(this.mobile_restrict_height && listHeight < bodyHeight){
15910 this.touchViewBodyEl.setHeight(listHeight);
15915 if(firstChecked && listHeight > bodyHeight){
15916 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15921 onTouchViewLoadException : function()
15923 this.hideTouchView();
15926 onTouchViewEmptyResults : function()
15928 this.clearTouchView();
15930 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15932 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15936 clearTouchView : function()
15938 this.touchViewListGroup.dom.innerHTML = '';
15941 onTouchViewClick : function(e, el, o)
15943 e.preventDefault();
15946 var rowIndex = o.rowIndex;
15948 var r = this.store.getAt(rowIndex);
15950 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15952 if(!this.multiple){
15953 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15954 c.dom.removeAttribute('checked');
15957 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15959 this.setFromData(r.data);
15961 var close = this.closeTriggerEl();
15967 this.hideTouchView();
15969 this.fireEvent('select', this, r, rowIndex);
15974 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15975 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15976 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15980 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15981 this.addItem(r.data);
15982 this.tickItems.push(r.data);
15986 getAutoCreateNativeIOS : function()
15989 cls: 'form-group' //input-group,
15994 cls : 'roo-ios-select'
15998 combobox.name = this.name;
16001 if (this.disabled) {
16002 combobox.disabled = true;
16005 var settings = this;
16007 ['xs','sm','md','lg'].map(function(size){
16008 if (settings[size]) {
16009 cfg.cls += ' col-' + size + '-' + settings[size];
16019 initIOSView : function()
16021 this.store.on('load', this.onIOSViewLoad, this);
16026 onIOSViewLoad : function()
16028 if(this.store.getCount() < 1){
16032 this.clearIOSView();
16034 if(this.allowBlank) {
16036 var default_text = '-- SELECT --';
16038 if(this.placeholder.length){
16039 default_text = this.placeholder;
16042 if(this.emptyTitle.length){
16043 default_text += ' - ' + this.emptyTitle + ' -';
16046 var opt = this.inputEl().createChild({
16049 html : default_text
16053 o[this.valueField] = 0;
16054 o[this.displayField] = default_text;
16056 this.ios_options.push({
16063 this.store.data.each(function(d, rowIndex){
16067 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16068 html = d.data[this.displayField];
16073 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16074 value = d.data[this.valueField];
16083 if(this.value == d.data[this.valueField]){
16084 option['selected'] = true;
16087 var opt = this.inputEl().createChild(option);
16089 this.ios_options.push({
16096 this.inputEl().on('change', function(){
16097 this.fireEvent('select', this);
16102 clearIOSView: function()
16104 this.inputEl().dom.innerHTML = '';
16106 this.ios_options = [];
16109 setIOSValue: function(v)
16113 if(!this.ios_options){
16117 Roo.each(this.ios_options, function(opts){
16119 opts.el.dom.removeAttribute('selected');
16121 if(opts.data[this.valueField] != v){
16125 opts.el.dom.setAttribute('selected', true);
16131 * @cfg {Boolean} grow
16135 * @cfg {Number} growMin
16139 * @cfg {Number} growMax
16148 Roo.apply(Roo.bootstrap.ComboBox, {
16152 cls: 'modal-header',
16174 cls: 'list-group-item',
16178 cls: 'roo-combobox-list-group-item-value'
16182 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16196 listItemCheckbox : {
16198 cls: 'list-group-item',
16202 cls: 'roo-combobox-list-group-item-value'
16206 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16222 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16227 cls: 'modal-footer',
16235 cls: 'col-xs-6 text-left',
16238 cls: 'btn btn-danger roo-touch-view-cancel',
16244 cls: 'col-xs-6 text-right',
16247 cls: 'btn btn-success roo-touch-view-ok',
16258 Roo.apply(Roo.bootstrap.ComboBox, {
16260 touchViewTemplate : {
16262 cls: 'modal fade roo-combobox-touch-view',
16266 cls: 'modal-dialog',
16267 style : 'position:fixed', // we have to fix position....
16271 cls: 'modal-content',
16273 Roo.bootstrap.ComboBox.header,
16274 Roo.bootstrap.ComboBox.body,
16275 Roo.bootstrap.ComboBox.footer
16284 * Ext JS Library 1.1.1
16285 * Copyright(c) 2006-2007, Ext JS, LLC.
16287 * Originally Released Under LGPL - original licence link has changed is not relivant.
16290 * <script type="text/javascript">
16295 * @extends Roo.util.Observable
16296 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16297 * This class also supports single and multi selection modes. <br>
16298 * Create a data model bound view:
16300 var store = new Roo.data.Store(...);
16302 var view = new Roo.View({
16304 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16306 singleSelect: true,
16307 selectedClass: "ydataview-selected",
16311 // listen for node click?
16312 view.on("click", function(vw, index, node, e){
16313 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16317 dataModel.load("foobar.xml");
16319 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16321 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16322 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16324 * Note: old style constructor is still suported (container, template, config)
16327 * Create a new View
16328 * @param {Object} config The config object
16331 Roo.View = function(config, depreciated_tpl, depreciated_config){
16333 this.parent = false;
16335 if (typeof(depreciated_tpl) == 'undefined') {
16336 // new way.. - universal constructor.
16337 Roo.apply(this, config);
16338 this.el = Roo.get(this.el);
16341 this.el = Roo.get(config);
16342 this.tpl = depreciated_tpl;
16343 Roo.apply(this, depreciated_config);
16345 this.wrapEl = this.el.wrap().wrap();
16346 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16349 if(typeof(this.tpl) == "string"){
16350 this.tpl = new Roo.Template(this.tpl);
16352 // support xtype ctors..
16353 this.tpl = new Roo.factory(this.tpl, Roo);
16357 this.tpl.compile();
16362 * @event beforeclick
16363 * Fires before a click is processed. Returns false to cancel the default action.
16364 * @param {Roo.View} this
16365 * @param {Number} index The index of the target node
16366 * @param {HTMLElement} node The target node
16367 * @param {Roo.EventObject} e The raw event object
16369 "beforeclick" : true,
16372 * Fires when a template node is clicked.
16373 * @param {Roo.View} this
16374 * @param {Number} index The index of the target node
16375 * @param {HTMLElement} node The target node
16376 * @param {Roo.EventObject} e The raw event object
16381 * Fires when a template node is double clicked.
16382 * @param {Roo.View} this
16383 * @param {Number} index The index of the target node
16384 * @param {HTMLElement} node The target node
16385 * @param {Roo.EventObject} e The raw event object
16389 * @event contextmenu
16390 * Fires when a template node is right clicked.
16391 * @param {Roo.View} this
16392 * @param {Number} index The index of the target node
16393 * @param {HTMLElement} node The target node
16394 * @param {Roo.EventObject} e The raw event object
16396 "contextmenu" : true,
16398 * @event selectionchange
16399 * Fires when the selected nodes change.
16400 * @param {Roo.View} this
16401 * @param {Array} selections Array of the selected nodes
16403 "selectionchange" : true,
16406 * @event beforeselect
16407 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16408 * @param {Roo.View} this
16409 * @param {HTMLElement} node The node to be selected
16410 * @param {Array} selections Array of currently selected nodes
16412 "beforeselect" : true,
16414 * @event preparedata
16415 * Fires on every row to render, to allow you to change the data.
16416 * @param {Roo.View} this
16417 * @param {Object} data to be rendered (change this)
16419 "preparedata" : true
16427 "click": this.onClick,
16428 "dblclick": this.onDblClick,
16429 "contextmenu": this.onContextMenu,
16433 this.selections = [];
16435 this.cmp = new Roo.CompositeElementLite([]);
16437 this.store = Roo.factory(this.store, Roo.data);
16438 this.setStore(this.store, true);
16441 if ( this.footer && this.footer.xtype) {
16443 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16445 this.footer.dataSource = this.store;
16446 this.footer.container = fctr;
16447 this.footer = Roo.factory(this.footer, Roo);
16448 fctr.insertFirst(this.el);
16450 // this is a bit insane - as the paging toolbar seems to detach the el..
16451 // dom.parentNode.parentNode.parentNode
16452 // they get detached?
16456 Roo.View.superclass.constructor.call(this);
16461 Roo.extend(Roo.View, Roo.util.Observable, {
16464 * @cfg {Roo.data.Store} store Data store to load data from.
16469 * @cfg {String|Roo.Element} el The container element.
16474 * @cfg {String|Roo.Template} tpl The template used by this View
16478 * @cfg {String} dataName the named area of the template to use as the data area
16479 * Works with domtemplates roo-name="name"
16483 * @cfg {String} selectedClass The css class to add to selected nodes
16485 selectedClass : "x-view-selected",
16487 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16492 * @cfg {String} text to display on mask (default Loading)
16496 * @cfg {Boolean} multiSelect Allow multiple selection
16498 multiSelect : false,
16500 * @cfg {Boolean} singleSelect Allow single selection
16502 singleSelect: false,
16505 * @cfg {Boolean} toggleSelect - selecting
16507 toggleSelect : false,
16510 * @cfg {Boolean} tickable - selecting
16515 * Returns the element this view is bound to.
16516 * @return {Roo.Element}
16518 getEl : function(){
16519 return this.wrapEl;
16525 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16527 refresh : function(){
16528 //Roo.log('refresh');
16531 // if we are using something like 'domtemplate', then
16532 // the what gets used is:
16533 // t.applySubtemplate(NAME, data, wrapping data..)
16534 // the outer template then get' applied with
16535 // the store 'extra data'
16536 // and the body get's added to the
16537 // roo-name="data" node?
16538 // <span class='roo-tpl-{name}'></span> ?????
16542 this.clearSelections();
16543 this.el.update("");
16545 var records = this.store.getRange();
16546 if(records.length < 1) {
16548 // is this valid?? = should it render a template??
16550 this.el.update(this.emptyText);
16554 if (this.dataName) {
16555 this.el.update(t.apply(this.store.meta)); //????
16556 el = this.el.child('.roo-tpl-' + this.dataName);
16559 for(var i = 0, len = records.length; i < len; i++){
16560 var data = this.prepareData(records[i].data, i, records[i]);
16561 this.fireEvent("preparedata", this, data, i, records[i]);
16563 var d = Roo.apply({}, data);
16566 Roo.apply(d, {'roo-id' : Roo.id()});
16570 Roo.each(this.parent.item, function(item){
16571 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16574 Roo.apply(d, {'roo-data-checked' : 'checked'});
16578 html[html.length] = Roo.util.Format.trim(
16580 t.applySubtemplate(this.dataName, d, this.store.meta) :
16587 el.update(html.join(""));
16588 this.nodes = el.dom.childNodes;
16589 this.updateIndexes(0);
16594 * Function to override to reformat the data that is sent to
16595 * the template for each node.
16596 * DEPRICATED - use the preparedata event handler.
16597 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16598 * a JSON object for an UpdateManager bound view).
16600 prepareData : function(data, index, record)
16602 this.fireEvent("preparedata", this, data, index, record);
16606 onUpdate : function(ds, record){
16607 // Roo.log('on update');
16608 this.clearSelections();
16609 var index = this.store.indexOf(record);
16610 var n = this.nodes[index];
16611 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16612 n.parentNode.removeChild(n);
16613 this.updateIndexes(index, index);
16619 onAdd : function(ds, records, index)
16621 //Roo.log(['on Add', ds, records, index] );
16622 this.clearSelections();
16623 if(this.nodes.length == 0){
16627 var n = this.nodes[index];
16628 for(var i = 0, len = records.length; i < len; i++){
16629 var d = this.prepareData(records[i].data, i, records[i]);
16631 this.tpl.insertBefore(n, d);
16634 this.tpl.append(this.el, d);
16637 this.updateIndexes(index);
16640 onRemove : function(ds, record, index){
16641 // Roo.log('onRemove');
16642 this.clearSelections();
16643 var el = this.dataName ?
16644 this.el.child('.roo-tpl-' + this.dataName) :
16647 el.dom.removeChild(this.nodes[index]);
16648 this.updateIndexes(index);
16652 * Refresh an individual node.
16653 * @param {Number} index
16655 refreshNode : function(index){
16656 this.onUpdate(this.store, this.store.getAt(index));
16659 updateIndexes : function(startIndex, endIndex){
16660 var ns = this.nodes;
16661 startIndex = startIndex || 0;
16662 endIndex = endIndex || ns.length - 1;
16663 for(var i = startIndex; i <= endIndex; i++){
16664 ns[i].nodeIndex = i;
16669 * Changes the data store this view uses and refresh the view.
16670 * @param {Store} store
16672 setStore : function(store, initial){
16673 if(!initial && this.store){
16674 this.store.un("datachanged", this.refresh);
16675 this.store.un("add", this.onAdd);
16676 this.store.un("remove", this.onRemove);
16677 this.store.un("update", this.onUpdate);
16678 this.store.un("clear", this.refresh);
16679 this.store.un("beforeload", this.onBeforeLoad);
16680 this.store.un("load", this.onLoad);
16681 this.store.un("loadexception", this.onLoad);
16685 store.on("datachanged", this.refresh, this);
16686 store.on("add", this.onAdd, this);
16687 store.on("remove", this.onRemove, this);
16688 store.on("update", this.onUpdate, this);
16689 store.on("clear", this.refresh, this);
16690 store.on("beforeload", this.onBeforeLoad, this);
16691 store.on("load", this.onLoad, this);
16692 store.on("loadexception", this.onLoad, this);
16700 * onbeforeLoad - masks the loading area.
16703 onBeforeLoad : function(store,opts)
16705 //Roo.log('onBeforeLoad');
16707 this.el.update("");
16709 this.el.mask(this.mask ? this.mask : "Loading" );
16711 onLoad : function ()
16718 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16719 * @param {HTMLElement} node
16720 * @return {HTMLElement} The template node
16722 findItemFromChild : function(node){
16723 var el = this.dataName ?
16724 this.el.child('.roo-tpl-' + this.dataName,true) :
16727 if(!node || node.parentNode == el){
16730 var p = node.parentNode;
16731 while(p && p != el){
16732 if(p.parentNode == el){
16741 onClick : function(e){
16742 var item = this.findItemFromChild(e.getTarget());
16744 var index = this.indexOf(item);
16745 if(this.onItemClick(item, index, e) !== false){
16746 this.fireEvent("click", this, index, item, e);
16749 this.clearSelections();
16754 onContextMenu : function(e){
16755 var item = this.findItemFromChild(e.getTarget());
16757 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16762 onDblClick : function(e){
16763 var item = this.findItemFromChild(e.getTarget());
16765 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16769 onItemClick : function(item, index, e)
16771 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16774 if (this.toggleSelect) {
16775 var m = this.isSelected(item) ? 'unselect' : 'select';
16778 _t[m](item, true, false);
16781 if(this.multiSelect || this.singleSelect){
16782 if(this.multiSelect && e.shiftKey && this.lastSelection){
16783 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16785 this.select(item, this.multiSelect && e.ctrlKey);
16786 this.lastSelection = item;
16789 if(!this.tickable){
16790 e.preventDefault();
16798 * Get the number of selected nodes.
16801 getSelectionCount : function(){
16802 return this.selections.length;
16806 * Get the currently selected nodes.
16807 * @return {Array} An array of HTMLElements
16809 getSelectedNodes : function(){
16810 return this.selections;
16814 * Get the indexes of the selected nodes.
16817 getSelectedIndexes : function(){
16818 var indexes = [], s = this.selections;
16819 for(var i = 0, len = s.length; i < len; i++){
16820 indexes.push(s[i].nodeIndex);
16826 * Clear all selections
16827 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16829 clearSelections : function(suppressEvent){
16830 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16831 this.cmp.elements = this.selections;
16832 this.cmp.removeClass(this.selectedClass);
16833 this.selections = [];
16834 if(!suppressEvent){
16835 this.fireEvent("selectionchange", this, this.selections);
16841 * Returns true if the passed node is selected
16842 * @param {HTMLElement/Number} node The node or node index
16843 * @return {Boolean}
16845 isSelected : function(node){
16846 var s = this.selections;
16850 node = this.getNode(node);
16851 return s.indexOf(node) !== -1;
16856 * @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
16857 * @param {Boolean} keepExisting (optional) true to keep existing selections
16858 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16860 select : function(nodeInfo, keepExisting, suppressEvent){
16861 if(nodeInfo instanceof Array){
16863 this.clearSelections(true);
16865 for(var i = 0, len = nodeInfo.length; i < len; i++){
16866 this.select(nodeInfo[i], true, true);
16870 var node = this.getNode(nodeInfo);
16871 if(!node || this.isSelected(node)){
16872 return; // already selected.
16875 this.clearSelections(true);
16878 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16879 Roo.fly(node).addClass(this.selectedClass);
16880 this.selections.push(node);
16881 if(!suppressEvent){
16882 this.fireEvent("selectionchange", this, this.selections);
16890 * @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
16891 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16892 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16894 unselect : function(nodeInfo, keepExisting, suppressEvent)
16896 if(nodeInfo instanceof Array){
16897 Roo.each(this.selections, function(s) {
16898 this.unselect(s, nodeInfo);
16902 var node = this.getNode(nodeInfo);
16903 if(!node || !this.isSelected(node)){
16904 //Roo.log("not selected");
16905 return; // not selected.
16909 Roo.each(this.selections, function(s) {
16911 Roo.fly(node).removeClass(this.selectedClass);
16918 this.selections= ns;
16919 this.fireEvent("selectionchange", this, this.selections);
16923 * Gets a template node.
16924 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16925 * @return {HTMLElement} The node or null if it wasn't found
16927 getNode : function(nodeInfo){
16928 if(typeof nodeInfo == "string"){
16929 return document.getElementById(nodeInfo);
16930 }else if(typeof nodeInfo == "number"){
16931 return this.nodes[nodeInfo];
16937 * Gets a range template nodes.
16938 * @param {Number} startIndex
16939 * @param {Number} endIndex
16940 * @return {Array} An array of nodes
16942 getNodes : function(start, end){
16943 var ns = this.nodes;
16944 start = start || 0;
16945 end = typeof end == "undefined" ? ns.length - 1 : end;
16948 for(var i = start; i <= end; i++){
16952 for(var i = start; i >= end; i--){
16960 * Finds the index of the passed node
16961 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16962 * @return {Number} The index of the node or -1
16964 indexOf : function(node){
16965 node = this.getNode(node);
16966 if(typeof node.nodeIndex == "number"){
16967 return node.nodeIndex;
16969 var ns = this.nodes;
16970 for(var i = 0, len = ns.length; i < len; i++){
16981 * based on jquery fullcalendar
16985 Roo.bootstrap = Roo.bootstrap || {};
16987 * @class Roo.bootstrap.Calendar
16988 * @extends Roo.bootstrap.Component
16989 * Bootstrap Calendar class
16990 * @cfg {Boolean} loadMask (true|false) default false
16991 * @cfg {Object} header generate the user specific header of the calendar, default false
16994 * Create a new Container
16995 * @param {Object} config The config object
17000 Roo.bootstrap.Calendar = function(config){
17001 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
17005 * Fires when a date is selected
17006 * @param {DatePicker} this
17007 * @param {Date} date The selected date
17011 * @event monthchange
17012 * Fires when the displayed month changes
17013 * @param {DatePicker} this
17014 * @param {Date} date The selected month
17016 'monthchange': true,
17018 * @event evententer
17019 * Fires when mouse over an event
17020 * @param {Calendar} this
17021 * @param {event} Event
17023 'evententer': true,
17025 * @event eventleave
17026 * Fires when the mouse leaves an
17027 * @param {Calendar} this
17030 'eventleave': true,
17032 * @event eventclick
17033 * Fires when the mouse click an
17034 * @param {Calendar} this
17043 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
17046 * @cfg {Number} startDay
17047 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17055 getAutoCreate : function(){
17058 var fc_button = function(name, corner, style, content ) {
17059 return Roo.apply({},{
17061 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
17063 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17066 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17077 style : 'width:100%',
17084 cls : 'fc-header-left',
17086 fc_button('prev', 'left', 'arrow', '‹' ),
17087 fc_button('next', 'right', 'arrow', '›' ),
17088 { tag: 'span', cls: 'fc-header-space' },
17089 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
17097 cls : 'fc-header-center',
17101 cls: 'fc-header-title',
17104 html : 'month / year'
17112 cls : 'fc-header-right',
17114 /* fc_button('month', 'left', '', 'month' ),
17115 fc_button('week', '', '', 'week' ),
17116 fc_button('day', 'right', '', 'day' )
17128 header = this.header;
17131 var cal_heads = function() {
17133 // fixme - handle this.
17135 for (var i =0; i < Date.dayNames.length; i++) {
17136 var d = Date.dayNames[i];
17139 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17140 html : d.substring(0,3)
17144 ret[0].cls += ' fc-first';
17145 ret[6].cls += ' fc-last';
17148 var cal_cell = function(n) {
17151 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17156 cls: 'fc-day-number',
17160 cls: 'fc-day-content',
17164 style: 'position: relative;' // height: 17px;
17176 var cal_rows = function() {
17179 for (var r = 0; r < 6; r++) {
17186 for (var i =0; i < Date.dayNames.length; i++) {
17187 var d = Date.dayNames[i];
17188 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17191 row.cn[0].cls+=' fc-first';
17192 row.cn[0].cn[0].style = 'min-height:90px';
17193 row.cn[6].cls+=' fc-last';
17197 ret[0].cls += ' fc-first';
17198 ret[4].cls += ' fc-prev-last';
17199 ret[5].cls += ' fc-last';
17206 cls: 'fc-border-separate',
17207 style : 'width:100%',
17215 cls : 'fc-first fc-last',
17233 cls : 'fc-content',
17234 style : "position: relative;",
17237 cls : 'fc-view fc-view-month fc-grid',
17238 style : 'position: relative',
17239 unselectable : 'on',
17242 cls : 'fc-event-container',
17243 style : 'position:absolute;z-index:8;top:0;left:0;'
17261 initEvents : function()
17264 throw "can not find store for calendar";
17270 style: "text-align:center",
17274 style: "background-color:white;width:50%;margin:250 auto",
17278 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
17289 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17291 var size = this.el.select('.fc-content', true).first().getSize();
17292 this.maskEl.setSize(size.width, size.height);
17293 this.maskEl.enableDisplayMode("block");
17294 if(!this.loadMask){
17295 this.maskEl.hide();
17298 this.store = Roo.factory(this.store, Roo.data);
17299 this.store.on('load', this.onLoad, this);
17300 this.store.on('beforeload', this.onBeforeLoad, this);
17304 this.cells = this.el.select('.fc-day',true);
17305 //Roo.log(this.cells);
17306 this.textNodes = this.el.query('.fc-day-number');
17307 this.cells.addClassOnOver('fc-state-hover');
17309 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17310 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17311 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17312 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17314 this.on('monthchange', this.onMonthChange, this);
17316 this.update(new Date().clearTime());
17319 resize : function() {
17320 var sz = this.el.getSize();
17322 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17323 this.el.select('.fc-day-content div',true).setHeight(34);
17328 showPrevMonth : function(e){
17329 this.update(this.activeDate.add("mo", -1));
17331 showToday : function(e){
17332 this.update(new Date().clearTime());
17335 showNextMonth : function(e){
17336 this.update(this.activeDate.add("mo", 1));
17340 showPrevYear : function(){
17341 this.update(this.activeDate.add("y", -1));
17345 showNextYear : function(){
17346 this.update(this.activeDate.add("y", 1));
17351 update : function(date)
17353 var vd = this.activeDate;
17354 this.activeDate = date;
17355 // if(vd && this.el){
17356 // var t = date.getTime();
17357 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17358 // Roo.log('using add remove');
17360 // this.fireEvent('monthchange', this, date);
17362 // this.cells.removeClass("fc-state-highlight");
17363 // this.cells.each(function(c){
17364 // if(c.dateValue == t){
17365 // c.addClass("fc-state-highlight");
17366 // setTimeout(function(){
17367 // try{c.dom.firstChild.focus();}catch(e){}
17377 var days = date.getDaysInMonth();
17379 var firstOfMonth = date.getFirstDateOfMonth();
17380 var startingPos = firstOfMonth.getDay()-this.startDay;
17382 if(startingPos < this.startDay){
17386 var pm = date.add(Date.MONTH, -1);
17387 var prevStart = pm.getDaysInMonth()-startingPos;
17389 this.cells = this.el.select('.fc-day',true);
17390 this.textNodes = this.el.query('.fc-day-number');
17391 this.cells.addClassOnOver('fc-state-hover');
17393 var cells = this.cells.elements;
17394 var textEls = this.textNodes;
17396 Roo.each(cells, function(cell){
17397 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17400 days += startingPos;
17402 // convert everything to numbers so it's fast
17403 var day = 86400000;
17404 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17407 //Roo.log(prevStart);
17409 var today = new Date().clearTime().getTime();
17410 var sel = date.clearTime().getTime();
17411 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17412 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17413 var ddMatch = this.disabledDatesRE;
17414 var ddText = this.disabledDatesText;
17415 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17416 var ddaysText = this.disabledDaysText;
17417 var format = this.format;
17419 var setCellClass = function(cal, cell){
17423 //Roo.log('set Cell Class');
17425 var t = d.getTime();
17429 cell.dateValue = t;
17431 cell.className += " fc-today";
17432 cell.className += " fc-state-highlight";
17433 cell.title = cal.todayText;
17436 // disable highlight in other month..
17437 //cell.className += " fc-state-highlight";
17442 cell.className = " fc-state-disabled";
17443 cell.title = cal.minText;
17447 cell.className = " fc-state-disabled";
17448 cell.title = cal.maxText;
17452 if(ddays.indexOf(d.getDay()) != -1){
17453 cell.title = ddaysText;
17454 cell.className = " fc-state-disabled";
17457 if(ddMatch && format){
17458 var fvalue = d.dateFormat(format);
17459 if(ddMatch.test(fvalue)){
17460 cell.title = ddText.replace("%0", fvalue);
17461 cell.className = " fc-state-disabled";
17465 if (!cell.initialClassName) {
17466 cell.initialClassName = cell.dom.className;
17469 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17474 for(; i < startingPos; i++) {
17475 textEls[i].innerHTML = (++prevStart);
17476 d.setDate(d.getDate()+1);
17478 cells[i].className = "fc-past fc-other-month";
17479 setCellClass(this, cells[i]);
17484 for(; i < days; i++){
17485 intDay = i - startingPos + 1;
17486 textEls[i].innerHTML = (intDay);
17487 d.setDate(d.getDate()+1);
17489 cells[i].className = ''; // "x-date-active";
17490 setCellClass(this, cells[i]);
17494 for(; i < 42; i++) {
17495 textEls[i].innerHTML = (++extraDays);
17496 d.setDate(d.getDate()+1);
17498 cells[i].className = "fc-future fc-other-month";
17499 setCellClass(this, cells[i]);
17502 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17504 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17506 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17507 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17509 if(totalRows != 6){
17510 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17511 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17514 this.fireEvent('monthchange', this, date);
17518 if(!this.internalRender){
17519 var main = this.el.dom.firstChild;
17520 var w = main.offsetWidth;
17521 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17522 Roo.fly(main).setWidth(w);
17523 this.internalRender = true;
17524 // opera does not respect the auto grow header center column
17525 // then, after it gets a width opera refuses to recalculate
17526 // without a second pass
17527 if(Roo.isOpera && !this.secondPass){
17528 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17529 this.secondPass = true;
17530 this.update.defer(10, this, [date]);
17537 findCell : function(dt) {
17538 dt = dt.clearTime().getTime();
17540 this.cells.each(function(c){
17541 //Roo.log("check " +c.dateValue + '?=' + dt);
17542 if(c.dateValue == dt){
17552 findCells : function(ev) {
17553 var s = ev.start.clone().clearTime().getTime();
17555 var e= ev.end.clone().clearTime().getTime();
17558 this.cells.each(function(c){
17559 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17561 if(c.dateValue > e){
17564 if(c.dateValue < s){
17573 // findBestRow: function(cells)
17577 // for (var i =0 ; i < cells.length;i++) {
17578 // ret = Math.max(cells[i].rows || 0,ret);
17585 addItem : function(ev)
17587 // look for vertical location slot in
17588 var cells = this.findCells(ev);
17590 // ev.row = this.findBestRow(cells);
17592 // work out the location.
17596 for(var i =0; i < cells.length; i++) {
17598 cells[i].row = cells[0].row;
17601 cells[i].row = cells[i].row + 1;
17611 if (crow.start.getY() == cells[i].getY()) {
17613 crow.end = cells[i];
17630 cells[0].events.push(ev);
17632 this.calevents.push(ev);
17635 clearEvents: function() {
17637 if(!this.calevents){
17641 Roo.each(this.cells.elements, function(c){
17647 Roo.each(this.calevents, function(e) {
17648 Roo.each(e.els, function(el) {
17649 el.un('mouseenter' ,this.onEventEnter, this);
17650 el.un('mouseleave' ,this.onEventLeave, this);
17655 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17661 renderEvents: function()
17665 this.cells.each(function(c) {
17674 if(c.row != c.events.length){
17675 r = 4 - (4 - (c.row - c.events.length));
17678 c.events = ev.slice(0, r);
17679 c.more = ev.slice(r);
17681 if(c.more.length && c.more.length == 1){
17682 c.events.push(c.more.pop());
17685 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17689 this.cells.each(function(c) {
17691 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17694 for (var e = 0; e < c.events.length; e++){
17695 var ev = c.events[e];
17696 var rows = ev.rows;
17698 for(var i = 0; i < rows.length; i++) {
17700 // how many rows should it span..
17703 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17704 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17706 unselectable : "on",
17709 cls: 'fc-event-inner',
17713 // cls: 'fc-event-time',
17714 // html : cells.length > 1 ? '' : ev.time
17718 cls: 'fc-event-title',
17719 html : String.format('{0}', ev.title)
17726 cls: 'ui-resizable-handle ui-resizable-e',
17727 html : '  '
17734 cfg.cls += ' fc-event-start';
17736 if ((i+1) == rows.length) {
17737 cfg.cls += ' fc-event-end';
17740 var ctr = _this.el.select('.fc-event-container',true).first();
17741 var cg = ctr.createChild(cfg);
17743 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17744 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17746 var r = (c.more.length) ? 1 : 0;
17747 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17748 cg.setWidth(ebox.right - sbox.x -2);
17750 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17751 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17752 cg.on('click', _this.onEventClick, _this, ev);
17763 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17764 style : 'position: absolute',
17765 unselectable : "on",
17768 cls: 'fc-event-inner',
17772 cls: 'fc-event-title',
17780 cls: 'ui-resizable-handle ui-resizable-e',
17781 html : '  '
17787 var ctr = _this.el.select('.fc-event-container',true).first();
17788 var cg = ctr.createChild(cfg);
17790 var sbox = c.select('.fc-day-content',true).first().getBox();
17791 var ebox = c.select('.fc-day-content',true).first().getBox();
17793 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17794 cg.setWidth(ebox.right - sbox.x -2);
17796 cg.on('click', _this.onMoreEventClick, _this, c.more);
17806 onEventEnter: function (e, el,event,d) {
17807 this.fireEvent('evententer', this, el, event);
17810 onEventLeave: function (e, el,event,d) {
17811 this.fireEvent('eventleave', this, el, event);
17814 onEventClick: function (e, el,event,d) {
17815 this.fireEvent('eventclick', this, el, event);
17818 onMonthChange: function () {
17822 onMoreEventClick: function(e, el, more)
17826 this.calpopover.placement = 'right';
17827 this.calpopover.setTitle('More');
17829 this.calpopover.setContent('');
17831 var ctr = this.calpopover.el.select('.popover-content', true).first();
17833 Roo.each(more, function(m){
17835 cls : 'fc-event-hori fc-event-draggable',
17838 var cg = ctr.createChild(cfg);
17840 cg.on('click', _this.onEventClick, _this, m);
17843 this.calpopover.show(el);
17848 onLoad: function ()
17850 this.calevents = [];
17853 if(this.store.getCount() > 0){
17854 this.store.data.each(function(d){
17857 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17858 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17859 time : d.data.start_time,
17860 title : d.data.title,
17861 description : d.data.description,
17862 venue : d.data.venue
17867 this.renderEvents();
17869 if(this.calevents.length && this.loadMask){
17870 this.maskEl.hide();
17874 onBeforeLoad: function()
17876 this.clearEvents();
17878 this.maskEl.show();
17892 * @class Roo.bootstrap.Popover
17893 * @extends Roo.bootstrap.Component
17894 * Bootstrap Popover class
17895 * @cfg {String} html contents of the popover (or false to use children..)
17896 * @cfg {String} title of popover (or false to hide)
17897 * @cfg {String} placement how it is placed
17898 * @cfg {String} trigger click || hover (or false to trigger manually)
17899 * @cfg {String} over what (parent or false to trigger manually.)
17900 * @cfg {Number} delay - delay before showing
17903 * Create a new Popover
17904 * @param {Object} config The config object
17907 Roo.bootstrap.Popover = function(config){
17908 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17914 * After the popover show
17916 * @param {Roo.bootstrap.Popover} this
17921 * After the popover hide
17923 * @param {Roo.bootstrap.Popover} this
17929 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17931 title: 'Fill in a title',
17934 placement : 'right',
17935 trigger : 'hover', // hover
17941 can_build_overlaid : false,
17943 getChildContainer : function()
17945 return this.el.select('.popover-content',true).first();
17948 getAutoCreate : function(){
17951 cls : 'popover roo-dynamic',
17952 style: 'display:block',
17958 cls : 'popover-inner',
17962 cls: 'popover-title popover-header',
17966 cls : 'popover-content popover-body',
17977 setTitle: function(str)
17980 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17982 setContent: function(str)
17985 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17987 // as it get's added to the bottom of the page.
17988 onRender : function(ct, position)
17990 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17992 var cfg = Roo.apply({}, this.getAutoCreate());
17996 cfg.cls += ' ' + this.cls;
17999 cfg.style = this.style;
18001 //Roo.log("adding to ");
18002 this.el = Roo.get(document.body).createChild(cfg, position);
18003 // Roo.log(this.el);
18008 initEvents : function()
18010 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
18011 this.el.enableDisplayMode('block');
18013 if (this.over === false) {
18016 if (this.triggers === false) {
18019 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18020 var triggers = this.trigger ? this.trigger.split(' ') : [];
18021 Roo.each(triggers, function(trigger) {
18023 if (trigger == 'click') {
18024 on_el.on('click', this.toggle, this);
18025 } else if (trigger != 'manual') {
18026 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
18027 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
18029 on_el.on(eventIn ,this.enter, this);
18030 on_el.on(eventOut, this.leave, this);
18041 toggle : function () {
18042 this.hoverState == 'in' ? this.leave() : this.enter();
18045 enter : function () {
18047 clearTimeout(this.timeout);
18049 this.hoverState = 'in';
18051 if (!this.delay || !this.delay.show) {
18056 this.timeout = setTimeout(function () {
18057 if (_t.hoverState == 'in') {
18060 }, this.delay.show)
18063 leave : function() {
18064 clearTimeout(this.timeout);
18066 this.hoverState = 'out';
18068 if (!this.delay || !this.delay.hide) {
18073 this.timeout = setTimeout(function () {
18074 if (_t.hoverState == 'out') {
18077 }, this.delay.hide)
18080 show : function (on_el)
18083 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18087 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18088 if (this.html !== false) {
18089 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18091 this.el.removeClass([
18092 'fade','top','bottom', 'left', 'right','in',
18093 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18095 if (!this.title.length) {
18096 this.el.select('.popover-title',true).hide();
18099 var placement = typeof this.placement == 'function' ?
18100 this.placement.call(this, this.el, on_el) :
18103 var autoToken = /\s?auto?\s?/i;
18104 var autoPlace = autoToken.test(placement);
18106 placement = placement.replace(autoToken, '') || 'top';
18110 //this.el.setXY([0,0]);
18112 this.el.dom.style.display='block';
18113 this.el.addClass(placement);
18115 //this.el.appendTo(on_el);
18117 var p = this.getPosition();
18118 var box = this.el.getBox();
18123 var align = Roo.bootstrap.Popover.alignment[placement];
18126 this.el.alignTo(on_el, align[0],align[1]);
18127 //var arrow = this.el.select('.arrow',true).first();
18128 //arrow.set(align[2],
18130 this.el.addClass('in');
18133 if (this.el.hasClass('fade')) {
18137 this.hoverState = 'in';
18139 this.fireEvent('show', this);
18144 this.el.setXY([0,0]);
18145 this.el.removeClass('in');
18147 this.hoverState = null;
18149 this.fireEvent('hide', this);
18154 Roo.bootstrap.Popover.alignment = {
18155 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18156 'right' : ['l-r', [10,0], 'left bs-popover-left'],
18157 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18158 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18169 * @class Roo.bootstrap.Progress
18170 * @extends Roo.bootstrap.Component
18171 * Bootstrap Progress class
18172 * @cfg {Boolean} striped striped of the progress bar
18173 * @cfg {Boolean} active animated of the progress bar
18177 * Create a new Progress
18178 * @param {Object} config The config object
18181 Roo.bootstrap.Progress = function(config){
18182 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18185 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
18190 getAutoCreate : function(){
18198 cfg.cls += ' progress-striped';
18202 cfg.cls += ' active';
18221 * @class Roo.bootstrap.ProgressBar
18222 * @extends Roo.bootstrap.Component
18223 * Bootstrap ProgressBar class
18224 * @cfg {Number} aria_valuenow aria-value now
18225 * @cfg {Number} aria_valuemin aria-value min
18226 * @cfg {Number} aria_valuemax aria-value max
18227 * @cfg {String} label label for the progress bar
18228 * @cfg {String} panel (success | info | warning | danger )
18229 * @cfg {String} role role of the progress bar
18230 * @cfg {String} sr_only text
18234 * Create a new ProgressBar
18235 * @param {Object} config The config object
18238 Roo.bootstrap.ProgressBar = function(config){
18239 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18242 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
18246 aria_valuemax : 100,
18252 getAutoCreate : function()
18257 cls: 'progress-bar',
18258 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18270 cfg.role = this.role;
18273 if(this.aria_valuenow){
18274 cfg['aria-valuenow'] = this.aria_valuenow;
18277 if(this.aria_valuemin){
18278 cfg['aria-valuemin'] = this.aria_valuemin;
18281 if(this.aria_valuemax){
18282 cfg['aria-valuemax'] = this.aria_valuemax;
18285 if(this.label && !this.sr_only){
18286 cfg.html = this.label;
18290 cfg.cls += ' progress-bar-' + this.panel;
18296 update : function(aria_valuenow)
18298 this.aria_valuenow = aria_valuenow;
18300 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18315 * @class Roo.bootstrap.TabGroup
18316 * @extends Roo.bootstrap.Column
18317 * Bootstrap Column class
18318 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18319 * @cfg {Boolean} carousel true to make the group behave like a carousel
18320 * @cfg {Boolean} bullets show bullets for the panels
18321 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18322 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18323 * @cfg {Boolean} showarrow (true|false) show arrow default true
18326 * Create a new TabGroup
18327 * @param {Object} config The config object
18330 Roo.bootstrap.TabGroup = function(config){
18331 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18333 this.navId = Roo.id();
18336 Roo.bootstrap.TabGroup.register(this);
18340 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18343 transition : false,
18348 slideOnTouch : false,
18351 getAutoCreate : function()
18353 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18355 cfg.cls += ' tab-content';
18357 if (this.carousel) {
18358 cfg.cls += ' carousel slide';
18361 cls : 'carousel-inner',
18365 if(this.bullets && !Roo.isTouch){
18368 cls : 'carousel-bullets',
18372 if(this.bullets_cls){
18373 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18380 cfg.cn[0].cn.push(bullets);
18383 if(this.showarrow){
18384 cfg.cn[0].cn.push({
18386 class : 'carousel-arrow',
18390 class : 'carousel-prev',
18394 class : 'fa fa-chevron-left'
18400 class : 'carousel-next',
18404 class : 'fa fa-chevron-right'
18417 initEvents: function()
18419 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18420 // this.el.on("touchstart", this.onTouchStart, this);
18423 if(this.autoslide){
18426 this.slideFn = window.setInterval(function() {
18427 _this.showPanelNext();
18431 if(this.showarrow){
18432 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18433 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18439 // onTouchStart : function(e, el, o)
18441 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18445 // this.showPanelNext();
18449 getChildContainer : function()
18451 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18455 * register a Navigation item
18456 * @param {Roo.bootstrap.NavItem} the navitem to add
18458 register : function(item)
18460 this.tabs.push( item);
18461 item.navId = this.navId; // not really needed..
18466 getActivePanel : function()
18469 Roo.each(this.tabs, function(t) {
18479 getPanelByName : function(n)
18482 Roo.each(this.tabs, function(t) {
18483 if (t.tabId == n) {
18491 indexOfPanel : function(p)
18494 Roo.each(this.tabs, function(t,i) {
18495 if (t.tabId == p.tabId) {
18504 * show a specific panel
18505 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18506 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18508 showPanel : function (pan)
18510 if(this.transition || typeof(pan) == 'undefined'){
18511 Roo.log("waiting for the transitionend");
18515 if (typeof(pan) == 'number') {
18516 pan = this.tabs[pan];
18519 if (typeof(pan) == 'string') {
18520 pan = this.getPanelByName(pan);
18523 var cur = this.getActivePanel();
18526 Roo.log('pan or acitve pan is undefined');
18530 if (pan.tabId == this.getActivePanel().tabId) {
18534 if (false === cur.fireEvent('beforedeactivate')) {
18538 if(this.bullets > 0 && !Roo.isTouch){
18539 this.setActiveBullet(this.indexOfPanel(pan));
18542 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18544 //class="carousel-item carousel-item-next carousel-item-left"
18546 this.transition = true;
18547 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18548 var lr = dir == 'next' ? 'left' : 'right';
18549 pan.el.addClass(dir); // or prev
18550 pan.el.addClass('carousel-item-' + dir); // or prev
18551 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18552 cur.el.addClass(lr); // or right
18553 pan.el.addClass(lr);
18554 cur.el.addClass('carousel-item-' +lr); // or right
18555 pan.el.addClass('carousel-item-' +lr);
18559 cur.el.on('transitionend', function() {
18560 Roo.log("trans end?");
18562 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18563 pan.setActive(true);
18565 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18566 cur.setActive(false);
18568 _this.transition = false;
18570 }, this, { single: true } );
18575 cur.setActive(false);
18576 pan.setActive(true);
18581 showPanelNext : function()
18583 var i = this.indexOfPanel(this.getActivePanel());
18585 if (i >= this.tabs.length - 1 && !this.autoslide) {
18589 if (i >= this.tabs.length - 1 && this.autoslide) {
18593 this.showPanel(this.tabs[i+1]);
18596 showPanelPrev : function()
18598 var i = this.indexOfPanel(this.getActivePanel());
18600 if (i < 1 && !this.autoslide) {
18604 if (i < 1 && this.autoslide) {
18605 i = this.tabs.length;
18608 this.showPanel(this.tabs[i-1]);
18612 addBullet: function()
18614 if(!this.bullets || Roo.isTouch){
18617 var ctr = this.el.select('.carousel-bullets',true).first();
18618 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18619 var bullet = ctr.createChild({
18620 cls : 'bullet bullet-' + i
18621 },ctr.dom.lastChild);
18626 bullet.on('click', (function(e, el, o, ii, t){
18628 e.preventDefault();
18630 this.showPanel(ii);
18632 if(this.autoslide && this.slideFn){
18633 clearInterval(this.slideFn);
18634 this.slideFn = window.setInterval(function() {
18635 _this.showPanelNext();
18639 }).createDelegate(this, [i, bullet], true));
18644 setActiveBullet : function(i)
18650 Roo.each(this.el.select('.bullet', true).elements, function(el){
18651 el.removeClass('selected');
18654 var bullet = this.el.select('.bullet-' + i, true).first();
18660 bullet.addClass('selected');
18671 Roo.apply(Roo.bootstrap.TabGroup, {
18675 * register a Navigation Group
18676 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18678 register : function(navgrp)
18680 this.groups[navgrp.navId] = navgrp;
18684 * fetch a Navigation Group based on the navigation ID
18685 * if one does not exist , it will get created.
18686 * @param {string} the navgroup to add
18687 * @returns {Roo.bootstrap.NavGroup} the navgroup
18689 get: function(navId) {
18690 if (typeof(this.groups[navId]) == 'undefined') {
18691 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18693 return this.groups[navId] ;
18708 * @class Roo.bootstrap.TabPanel
18709 * @extends Roo.bootstrap.Component
18710 * Bootstrap TabPanel class
18711 * @cfg {Boolean} active panel active
18712 * @cfg {String} html panel content
18713 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18714 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18715 * @cfg {String} href click to link..
18719 * Create a new TabPanel
18720 * @param {Object} config The config object
18723 Roo.bootstrap.TabPanel = function(config){
18724 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18728 * Fires when the active status changes
18729 * @param {Roo.bootstrap.TabPanel} this
18730 * @param {Boolean} state the new state
18735 * @event beforedeactivate
18736 * Fires before a tab is de-activated - can be used to do validation on a form.
18737 * @param {Roo.bootstrap.TabPanel} this
18738 * @return {Boolean} false if there is an error
18741 'beforedeactivate': true
18744 this.tabId = this.tabId || Roo.id();
18748 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18756 getAutoCreate : function(){
18761 // item is needed for carousel - not sure if it has any effect otherwise
18762 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18763 html: this.html || ''
18767 cfg.cls += ' active';
18771 cfg.tabId = this.tabId;
18779 initEvents: function()
18781 var p = this.parent();
18783 this.navId = this.navId || p.navId;
18785 if (typeof(this.navId) != 'undefined') {
18786 // not really needed.. but just in case.. parent should be a NavGroup.
18787 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18791 var i = tg.tabs.length - 1;
18793 if(this.active && tg.bullets > 0 && i < tg.bullets){
18794 tg.setActiveBullet(i);
18798 this.el.on('click', this.onClick, this);
18801 this.el.on("touchstart", this.onTouchStart, this);
18802 this.el.on("touchmove", this.onTouchMove, this);
18803 this.el.on("touchend", this.onTouchEnd, this);
18808 onRender : function(ct, position)
18810 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18813 setActive : function(state)
18815 Roo.log("panel - set active " + this.tabId + "=" + state);
18817 this.active = state;
18819 this.el.removeClass('active');
18821 } else if (!this.el.hasClass('active')) {
18822 this.el.addClass('active');
18825 this.fireEvent('changed', this, state);
18828 onClick : function(e)
18830 e.preventDefault();
18832 if(!this.href.length){
18836 window.location.href = this.href;
18845 onTouchStart : function(e)
18847 this.swiping = false;
18849 this.startX = e.browserEvent.touches[0].clientX;
18850 this.startY = e.browserEvent.touches[0].clientY;
18853 onTouchMove : function(e)
18855 this.swiping = true;
18857 this.endX = e.browserEvent.touches[0].clientX;
18858 this.endY = e.browserEvent.touches[0].clientY;
18861 onTouchEnd : function(e)
18868 var tabGroup = this.parent();
18870 if(this.endX > this.startX){ // swiping right
18871 tabGroup.showPanelPrev();
18875 if(this.startX > this.endX){ // swiping left
18876 tabGroup.showPanelNext();
18895 * @class Roo.bootstrap.DateField
18896 * @extends Roo.bootstrap.Input
18897 * Bootstrap DateField class
18898 * @cfg {Number} weekStart default 0
18899 * @cfg {String} viewMode default empty, (months|years)
18900 * @cfg {String} minViewMode default empty, (months|years)
18901 * @cfg {Number} startDate default -Infinity
18902 * @cfg {Number} endDate default Infinity
18903 * @cfg {Boolean} todayHighlight default false
18904 * @cfg {Boolean} todayBtn default false
18905 * @cfg {Boolean} calendarWeeks default false
18906 * @cfg {Object} daysOfWeekDisabled default empty
18907 * @cfg {Boolean} singleMode default false (true | false)
18909 * @cfg {Boolean} keyboardNavigation default true
18910 * @cfg {String} language default en
18913 * Create a new DateField
18914 * @param {Object} config The config object
18917 Roo.bootstrap.DateField = function(config){
18918 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18922 * Fires when this field show.
18923 * @param {Roo.bootstrap.DateField} this
18924 * @param {Mixed} date The date value
18929 * Fires when this field hide.
18930 * @param {Roo.bootstrap.DateField} this
18931 * @param {Mixed} date The date value
18936 * Fires when select a date.
18937 * @param {Roo.bootstrap.DateField} this
18938 * @param {Mixed} date The date value
18942 * @event beforeselect
18943 * Fires when before select a date.
18944 * @param {Roo.bootstrap.DateField} this
18945 * @param {Mixed} date The date value
18947 beforeselect : true
18951 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18954 * @cfg {String} format
18955 * The default date format string which can be overriden for localization support. The format must be
18956 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18960 * @cfg {String} altFormats
18961 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18962 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18964 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18972 todayHighlight : false,
18978 keyboardNavigation: true,
18980 calendarWeeks: false,
18982 startDate: -Infinity,
18986 daysOfWeekDisabled: [],
18990 singleMode : false,
18992 UTCDate: function()
18994 return new Date(Date.UTC.apply(Date, arguments));
18997 UTCToday: function()
18999 var today = new Date();
19000 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
19003 getDate: function() {
19004 var d = this.getUTCDate();
19005 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
19008 getUTCDate: function() {
19012 setDate: function(d) {
19013 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
19016 setUTCDate: function(d) {
19018 this.setValue(this.formatDate(this.date));
19021 onRender: function(ct, position)
19024 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
19026 this.language = this.language || 'en';
19027 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
19028 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
19030 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
19031 this.format = this.format || 'm/d/y';
19032 this.isInline = false;
19033 this.isInput = true;
19034 this.component = this.el.select('.add-on', true).first() || false;
19035 this.component = (this.component && this.component.length === 0) ? false : this.component;
19036 this.hasInput = this.component && this.inputEl().length;
19038 if (typeof(this.minViewMode === 'string')) {
19039 switch (this.minViewMode) {
19041 this.minViewMode = 1;
19044 this.minViewMode = 2;
19047 this.minViewMode = 0;
19052 if (typeof(this.viewMode === 'string')) {
19053 switch (this.viewMode) {
19066 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19068 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19070 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19072 this.picker().on('mousedown', this.onMousedown, this);
19073 this.picker().on('click', this.onClick, this);
19075 this.picker().addClass('datepicker-dropdown');
19077 this.startViewMode = this.viewMode;
19079 if(this.singleMode){
19080 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19081 v.setVisibilityMode(Roo.Element.DISPLAY);
19085 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19086 v.setStyle('width', '189px');
19090 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19091 if(!this.calendarWeeks){
19096 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19097 v.attr('colspan', function(i, val){
19098 return parseInt(val) + 1;
19103 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19105 this.setStartDate(this.startDate);
19106 this.setEndDate(this.endDate);
19108 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19115 if(this.isInline) {
19120 picker : function()
19122 return this.pickerEl;
19123 // return this.el.select('.datepicker', true).first();
19126 fillDow: function()
19128 var dowCnt = this.weekStart;
19137 if(this.calendarWeeks){
19145 while (dowCnt < this.weekStart + 7) {
19149 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19153 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19156 fillMonths: function()
19159 var months = this.picker().select('>.datepicker-months td', true).first();
19161 months.dom.innerHTML = '';
19167 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19170 months.createChild(month);
19177 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;
19179 if (this.date < this.startDate) {
19180 this.viewDate = new Date(this.startDate);
19181 } else if (this.date > this.endDate) {
19182 this.viewDate = new Date(this.endDate);
19184 this.viewDate = new Date(this.date);
19192 var d = new Date(this.viewDate),
19193 year = d.getUTCFullYear(),
19194 month = d.getUTCMonth(),
19195 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19196 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19197 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19198 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19199 currentDate = this.date && this.date.valueOf(),
19200 today = this.UTCToday();
19202 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19204 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19206 // this.picker.select('>tfoot th.today').
19207 // .text(dates[this.language].today)
19208 // .toggle(this.todayBtn !== false);
19210 this.updateNavArrows();
19213 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19215 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19217 prevMonth.setUTCDate(day);
19219 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19221 var nextMonth = new Date(prevMonth);
19223 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19225 nextMonth = nextMonth.valueOf();
19227 var fillMonths = false;
19229 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19231 while(prevMonth.valueOf() <= nextMonth) {
19234 if (prevMonth.getUTCDay() === this.weekStart) {
19236 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19244 if(this.calendarWeeks){
19245 // ISO 8601: First week contains first thursday.
19246 // ISO also states week starts on Monday, but we can be more abstract here.
19248 // Start of current week: based on weekstart/current date
19249 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19250 // Thursday of this week
19251 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19252 // First Thursday of year, year from thursday
19253 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19254 // Calendar week: ms between thursdays, div ms per day, div 7 days
19255 calWeek = (th - yth) / 864e5 / 7 + 1;
19257 fillMonths.cn.push({
19265 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19267 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19270 if (this.todayHighlight &&
19271 prevMonth.getUTCFullYear() == today.getFullYear() &&
19272 prevMonth.getUTCMonth() == today.getMonth() &&
19273 prevMonth.getUTCDate() == today.getDate()) {
19274 clsName += ' today';
19277 if (currentDate && prevMonth.valueOf() === currentDate) {
19278 clsName += ' active';
19281 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19282 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19283 clsName += ' disabled';
19286 fillMonths.cn.push({
19288 cls: 'day ' + clsName,
19289 html: prevMonth.getDate()
19292 prevMonth.setDate(prevMonth.getDate()+1);
19295 var currentYear = this.date && this.date.getUTCFullYear();
19296 var currentMonth = this.date && this.date.getUTCMonth();
19298 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19300 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19301 v.removeClass('active');
19303 if(currentYear === year && k === currentMonth){
19304 v.addClass('active');
19307 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19308 v.addClass('disabled');
19314 year = parseInt(year/10, 10) * 10;
19316 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19318 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19321 for (var i = -1; i < 11; i++) {
19322 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19324 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19332 showMode: function(dir)
19335 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19338 Roo.each(this.picker().select('>div',true).elements, function(v){
19339 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19342 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19347 if(this.isInline) {
19351 this.picker().removeClass(['bottom', 'top']);
19353 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19355 * place to the top of element!
19359 this.picker().addClass('top');
19360 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19365 this.picker().addClass('bottom');
19367 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19370 parseDate : function(value)
19372 if(!value || value instanceof Date){
19375 var v = Date.parseDate(value, this.format);
19376 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19377 v = Date.parseDate(value, 'Y-m-d');
19379 if(!v && this.altFormats){
19380 if(!this.altFormatsArray){
19381 this.altFormatsArray = this.altFormats.split("|");
19383 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19384 v = Date.parseDate(value, this.altFormatsArray[i]);
19390 formatDate : function(date, fmt)
19392 return (!date || !(date instanceof Date)) ?
19393 date : date.dateFormat(fmt || this.format);
19396 onFocus : function()
19398 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19402 onBlur : function()
19404 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19406 var d = this.inputEl().getValue();
19413 showPopup : function()
19415 this.picker().show();
19419 this.fireEvent('showpopup', this, this.date);
19422 hidePopup : function()
19424 if(this.isInline) {
19427 this.picker().hide();
19428 this.viewMode = this.startViewMode;
19431 this.fireEvent('hidepopup', this, this.date);
19435 onMousedown: function(e)
19437 e.stopPropagation();
19438 e.preventDefault();
19443 Roo.bootstrap.DateField.superclass.keyup.call(this);
19447 setValue: function(v)
19449 if(this.fireEvent('beforeselect', this, v) !== false){
19450 var d = new Date(this.parseDate(v) ).clearTime();
19452 if(isNaN(d.getTime())){
19453 this.date = this.viewDate = '';
19454 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19458 v = this.formatDate(d);
19460 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19462 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19466 this.fireEvent('select', this, this.date);
19470 getValue: function()
19472 return this.formatDate(this.date);
19475 fireKey: function(e)
19477 if (!this.picker().isVisible()){
19478 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19484 var dateChanged = false,
19486 newDate, newViewDate;
19491 e.preventDefault();
19495 if (!this.keyboardNavigation) {
19498 dir = e.keyCode == 37 ? -1 : 1;
19501 newDate = this.moveYear(this.date, dir);
19502 newViewDate = this.moveYear(this.viewDate, dir);
19503 } else if (e.shiftKey){
19504 newDate = this.moveMonth(this.date, dir);
19505 newViewDate = this.moveMonth(this.viewDate, dir);
19507 newDate = new Date(this.date);
19508 newDate.setUTCDate(this.date.getUTCDate() + dir);
19509 newViewDate = new Date(this.viewDate);
19510 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19512 if (this.dateWithinRange(newDate)){
19513 this.date = newDate;
19514 this.viewDate = newViewDate;
19515 this.setValue(this.formatDate(this.date));
19517 e.preventDefault();
19518 dateChanged = true;
19523 if (!this.keyboardNavigation) {
19526 dir = e.keyCode == 38 ? -1 : 1;
19528 newDate = this.moveYear(this.date, dir);
19529 newViewDate = this.moveYear(this.viewDate, dir);
19530 } else if (e.shiftKey){
19531 newDate = this.moveMonth(this.date, dir);
19532 newViewDate = this.moveMonth(this.viewDate, dir);
19534 newDate = new Date(this.date);
19535 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19536 newViewDate = new Date(this.viewDate);
19537 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19539 if (this.dateWithinRange(newDate)){
19540 this.date = newDate;
19541 this.viewDate = newViewDate;
19542 this.setValue(this.formatDate(this.date));
19544 e.preventDefault();
19545 dateChanged = true;
19549 this.setValue(this.formatDate(this.date));
19551 e.preventDefault();
19554 this.setValue(this.formatDate(this.date));
19568 onClick: function(e)
19570 e.stopPropagation();
19571 e.preventDefault();
19573 var target = e.getTarget();
19575 if(target.nodeName.toLowerCase() === 'i'){
19576 target = Roo.get(target).dom.parentNode;
19579 var nodeName = target.nodeName;
19580 var className = target.className;
19581 var html = target.innerHTML;
19582 //Roo.log(nodeName);
19584 switch(nodeName.toLowerCase()) {
19586 switch(className) {
19592 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19593 switch(this.viewMode){
19595 this.viewDate = this.moveMonth(this.viewDate, dir);
19599 this.viewDate = this.moveYear(this.viewDate, dir);
19605 var date = new Date();
19606 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19608 this.setValue(this.formatDate(this.date));
19615 if (className.indexOf('disabled') < 0) {
19616 this.viewDate.setUTCDate(1);
19617 if (className.indexOf('month') > -1) {
19618 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19620 var year = parseInt(html, 10) || 0;
19621 this.viewDate.setUTCFullYear(year);
19625 if(this.singleMode){
19626 this.setValue(this.formatDate(this.viewDate));
19637 //Roo.log(className);
19638 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19639 var day = parseInt(html, 10) || 1;
19640 var year = this.viewDate.getUTCFullYear(),
19641 month = this.viewDate.getUTCMonth();
19643 if (className.indexOf('old') > -1) {
19650 } else if (className.indexOf('new') > -1) {
19658 //Roo.log([year,month,day]);
19659 this.date = this.UTCDate(year, month, day,0,0,0,0);
19660 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19662 //Roo.log(this.formatDate(this.date));
19663 this.setValue(this.formatDate(this.date));
19670 setStartDate: function(startDate)
19672 this.startDate = startDate || -Infinity;
19673 if (this.startDate !== -Infinity) {
19674 this.startDate = this.parseDate(this.startDate);
19677 this.updateNavArrows();
19680 setEndDate: function(endDate)
19682 this.endDate = endDate || Infinity;
19683 if (this.endDate !== Infinity) {
19684 this.endDate = this.parseDate(this.endDate);
19687 this.updateNavArrows();
19690 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19692 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19693 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19694 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19696 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19697 return parseInt(d, 10);
19700 this.updateNavArrows();
19703 updateNavArrows: function()
19705 if(this.singleMode){
19709 var d = new Date(this.viewDate),
19710 year = d.getUTCFullYear(),
19711 month = d.getUTCMonth();
19713 Roo.each(this.picker().select('.prev', true).elements, function(v){
19715 switch (this.viewMode) {
19718 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19724 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19731 Roo.each(this.picker().select('.next', true).elements, function(v){
19733 switch (this.viewMode) {
19736 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19742 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19750 moveMonth: function(date, dir)
19755 var new_date = new Date(date.valueOf()),
19756 day = new_date.getUTCDate(),
19757 month = new_date.getUTCMonth(),
19758 mag = Math.abs(dir),
19760 dir = dir > 0 ? 1 : -1;
19763 // If going back one month, make sure month is not current month
19764 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19766 return new_date.getUTCMonth() == month;
19768 // If going forward one month, make sure month is as expected
19769 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19771 return new_date.getUTCMonth() != new_month;
19773 new_month = month + dir;
19774 new_date.setUTCMonth(new_month);
19775 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19776 if (new_month < 0 || new_month > 11) {
19777 new_month = (new_month + 12) % 12;
19780 // For magnitudes >1, move one month at a time...
19781 for (var i=0; i<mag; i++) {
19782 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19783 new_date = this.moveMonth(new_date, dir);
19785 // ...then reset the day, keeping it in the new month
19786 new_month = new_date.getUTCMonth();
19787 new_date.setUTCDate(day);
19789 return new_month != new_date.getUTCMonth();
19792 // Common date-resetting loop -- if date is beyond end of month, make it
19795 new_date.setUTCDate(--day);
19796 new_date.setUTCMonth(new_month);
19801 moveYear: function(date, dir)
19803 return this.moveMonth(date, dir*12);
19806 dateWithinRange: function(date)
19808 return date >= this.startDate && date <= this.endDate;
19814 this.picker().remove();
19817 validateValue : function(value)
19819 if(this.getVisibilityEl().hasClass('hidden')){
19823 if(value.length < 1) {
19824 if(this.allowBlank){
19830 if(value.length < this.minLength){
19833 if(value.length > this.maxLength){
19837 var vt = Roo.form.VTypes;
19838 if(!vt[this.vtype](value, this)){
19842 if(typeof this.validator == "function"){
19843 var msg = this.validator(value);
19849 if(this.regex && !this.regex.test(value)){
19853 if(typeof(this.parseDate(value)) == 'undefined'){
19857 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19861 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19871 this.date = this.viewDate = '';
19873 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19878 Roo.apply(Roo.bootstrap.DateField, {
19889 html: '<i class="fa fa-arrow-left"/>'
19899 html: '<i class="fa fa-arrow-right"/>'
19941 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19942 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19943 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19944 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19945 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19958 navFnc: 'FullYear',
19963 navFnc: 'FullYear',
19968 Roo.apply(Roo.bootstrap.DateField, {
19972 cls: 'datepicker dropdown-menu roo-dynamic',
19976 cls: 'datepicker-days',
19980 cls: 'table-condensed',
19982 Roo.bootstrap.DateField.head,
19986 Roo.bootstrap.DateField.footer
19993 cls: 'datepicker-months',
19997 cls: 'table-condensed',
19999 Roo.bootstrap.DateField.head,
20000 Roo.bootstrap.DateField.content,
20001 Roo.bootstrap.DateField.footer
20008 cls: 'datepicker-years',
20012 cls: 'table-condensed',
20014 Roo.bootstrap.DateField.head,
20015 Roo.bootstrap.DateField.content,
20016 Roo.bootstrap.DateField.footer
20035 * @class Roo.bootstrap.TimeField
20036 * @extends Roo.bootstrap.Input
20037 * Bootstrap DateField class
20041 * Create a new TimeField
20042 * @param {Object} config The config object
20045 Roo.bootstrap.TimeField = function(config){
20046 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20050 * Fires when this field show.
20051 * @param {Roo.bootstrap.DateField} thisthis
20052 * @param {Mixed} date The date value
20057 * Fires when this field hide.
20058 * @param {Roo.bootstrap.DateField} this
20059 * @param {Mixed} date The date value
20064 * Fires when select a date.
20065 * @param {Roo.bootstrap.DateField} this
20066 * @param {Mixed} date The date value
20072 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
20075 * @cfg {String} format
20076 * The default time format string which can be overriden for localization support. The format must be
20077 * valid according to {@link Date#parseDate} (defaults to 'H:i').
20081 onRender: function(ct, position)
20084 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20086 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20088 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20090 this.pop = this.picker().select('>.datepicker-time',true).first();
20091 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20093 this.picker().on('mousedown', this.onMousedown, this);
20094 this.picker().on('click', this.onClick, this);
20096 this.picker().addClass('datepicker-dropdown');
20101 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20102 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20103 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20104 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20105 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20106 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20110 fireKey: function(e){
20111 if (!this.picker().isVisible()){
20112 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20118 e.preventDefault();
20126 this.onTogglePeriod();
20129 this.onIncrementMinutes();
20132 this.onDecrementMinutes();
20141 onClick: function(e) {
20142 e.stopPropagation();
20143 e.preventDefault();
20146 picker : function()
20148 return this.el.select('.datepicker', true).first();
20151 fillTime: function()
20153 var time = this.pop.select('tbody', true).first();
20155 time.dom.innerHTML = '';
20170 cls: 'hours-up glyphicon glyphicon-chevron-up'
20190 cls: 'minutes-up glyphicon glyphicon-chevron-up'
20211 cls: 'timepicker-hour',
20226 cls: 'timepicker-minute',
20241 cls: 'btn btn-primary period',
20263 cls: 'hours-down glyphicon glyphicon-chevron-down'
20283 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20301 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20308 var hours = this.time.getHours();
20309 var minutes = this.time.getMinutes();
20322 hours = hours - 12;
20326 hours = '0' + hours;
20330 minutes = '0' + minutes;
20333 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20334 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20335 this.pop.select('button', true).first().dom.innerHTML = period;
20341 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20343 var cls = ['bottom'];
20345 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20352 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20357 this.picker().addClass(cls.join('-'));
20361 Roo.each(cls, function(c){
20363 _this.picker().setTop(_this.inputEl().getHeight());
20367 _this.picker().setTop(0 - _this.picker().getHeight());
20372 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20376 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20383 onFocus : function()
20385 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20389 onBlur : function()
20391 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20397 this.picker().show();
20402 this.fireEvent('show', this, this.date);
20407 this.picker().hide();
20410 this.fireEvent('hide', this, this.date);
20413 setTime : function()
20416 this.setValue(this.time.format(this.format));
20418 this.fireEvent('select', this, this.date);
20423 onMousedown: function(e){
20424 e.stopPropagation();
20425 e.preventDefault();
20428 onIncrementHours: function()
20430 Roo.log('onIncrementHours');
20431 this.time = this.time.add(Date.HOUR, 1);
20436 onDecrementHours: function()
20438 Roo.log('onDecrementHours');
20439 this.time = this.time.add(Date.HOUR, -1);
20443 onIncrementMinutes: function()
20445 Roo.log('onIncrementMinutes');
20446 this.time = this.time.add(Date.MINUTE, 1);
20450 onDecrementMinutes: function()
20452 Roo.log('onDecrementMinutes');
20453 this.time = this.time.add(Date.MINUTE, -1);
20457 onTogglePeriod: function()
20459 Roo.log('onTogglePeriod');
20460 this.time = this.time.add(Date.HOUR, 12);
20467 Roo.apply(Roo.bootstrap.TimeField, {
20497 cls: 'btn btn-info ok',
20509 Roo.apply(Roo.bootstrap.TimeField, {
20513 cls: 'datepicker dropdown-menu',
20517 cls: 'datepicker-time',
20521 cls: 'table-condensed',
20523 Roo.bootstrap.TimeField.content,
20524 Roo.bootstrap.TimeField.footer
20543 * @class Roo.bootstrap.MonthField
20544 * @extends Roo.bootstrap.Input
20545 * Bootstrap MonthField class
20547 * @cfg {String} language default en
20550 * Create a new MonthField
20551 * @param {Object} config The config object
20554 Roo.bootstrap.MonthField = function(config){
20555 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20560 * Fires when this field show.
20561 * @param {Roo.bootstrap.MonthField} this
20562 * @param {Mixed} date The date value
20567 * Fires when this field hide.
20568 * @param {Roo.bootstrap.MonthField} this
20569 * @param {Mixed} date The date value
20574 * Fires when select a date.
20575 * @param {Roo.bootstrap.MonthField} this
20576 * @param {String} oldvalue The old value
20577 * @param {String} newvalue The new value
20583 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20585 onRender: function(ct, position)
20588 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20590 this.language = this.language || 'en';
20591 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20592 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20594 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20595 this.isInline = false;
20596 this.isInput = true;
20597 this.component = this.el.select('.add-on', true).first() || false;
20598 this.component = (this.component && this.component.length === 0) ? false : this.component;
20599 this.hasInput = this.component && this.inputEL().length;
20601 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20603 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20605 this.picker().on('mousedown', this.onMousedown, this);
20606 this.picker().on('click', this.onClick, this);
20608 this.picker().addClass('datepicker-dropdown');
20610 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20611 v.setStyle('width', '189px');
20618 if(this.isInline) {
20624 setValue: function(v, suppressEvent)
20626 var o = this.getValue();
20628 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20632 if(suppressEvent !== true){
20633 this.fireEvent('select', this, o, v);
20638 getValue: function()
20643 onClick: function(e)
20645 e.stopPropagation();
20646 e.preventDefault();
20648 var target = e.getTarget();
20650 if(target.nodeName.toLowerCase() === 'i'){
20651 target = Roo.get(target).dom.parentNode;
20654 var nodeName = target.nodeName;
20655 var className = target.className;
20656 var html = target.innerHTML;
20658 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20662 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20664 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20670 picker : function()
20672 return this.pickerEl;
20675 fillMonths: function()
20678 var months = this.picker().select('>.datepicker-months td', true).first();
20680 months.dom.innerHTML = '';
20686 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20689 months.createChild(month);
20698 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20699 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20702 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20703 e.removeClass('active');
20705 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20706 e.addClass('active');
20713 if(this.isInline) {
20717 this.picker().removeClass(['bottom', 'top']);
20719 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20721 * place to the top of element!
20725 this.picker().addClass('top');
20726 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20731 this.picker().addClass('bottom');
20733 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20736 onFocus : function()
20738 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20742 onBlur : function()
20744 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20746 var d = this.inputEl().getValue();
20755 this.picker().show();
20756 this.picker().select('>.datepicker-months', true).first().show();
20760 this.fireEvent('show', this, this.date);
20765 if(this.isInline) {
20768 this.picker().hide();
20769 this.fireEvent('hide', this, this.date);
20773 onMousedown: function(e)
20775 e.stopPropagation();
20776 e.preventDefault();
20781 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20785 fireKey: function(e)
20787 if (!this.picker().isVisible()){
20788 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20799 e.preventDefault();
20803 dir = e.keyCode == 37 ? -1 : 1;
20805 this.vIndex = this.vIndex + dir;
20807 if(this.vIndex < 0){
20811 if(this.vIndex > 11){
20815 if(isNaN(this.vIndex)){
20819 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20825 dir = e.keyCode == 38 ? -1 : 1;
20827 this.vIndex = this.vIndex + dir * 4;
20829 if(this.vIndex < 0){
20833 if(this.vIndex > 11){
20837 if(isNaN(this.vIndex)){
20841 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20846 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20847 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20851 e.preventDefault();
20854 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20855 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20871 this.picker().remove();
20876 Roo.apply(Roo.bootstrap.MonthField, {
20895 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20896 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20901 Roo.apply(Roo.bootstrap.MonthField, {
20905 cls: 'datepicker dropdown-menu roo-dynamic',
20909 cls: 'datepicker-months',
20913 cls: 'table-condensed',
20915 Roo.bootstrap.DateField.content
20935 * @class Roo.bootstrap.CheckBox
20936 * @extends Roo.bootstrap.Input
20937 * Bootstrap CheckBox class
20939 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20940 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20941 * @cfg {String} boxLabel The text that appears beside the checkbox
20942 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20943 * @cfg {Boolean} checked initnal the element
20944 * @cfg {Boolean} inline inline the element (default false)
20945 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20946 * @cfg {String} tooltip label tooltip
20949 * Create a new CheckBox
20950 * @param {Object} config The config object
20953 Roo.bootstrap.CheckBox = function(config){
20954 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20959 * Fires when the element is checked or unchecked.
20960 * @param {Roo.bootstrap.CheckBox} this This input
20961 * @param {Boolean} checked The new checked value
20966 * Fires when the element is click.
20967 * @param {Roo.bootstrap.CheckBox} this This input
20974 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20976 inputType: 'checkbox',
20985 getAutoCreate : function()
20987 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20993 cfg.cls = 'form-group ' + this.inputType; //input-group
20996 cfg.cls += ' ' + this.inputType + '-inline';
21002 type : this.inputType,
21003 value : this.inputValue,
21004 cls : 'roo-' + this.inputType, //'form-box',
21005 placeholder : this.placeholder || ''
21009 if(this.inputType != 'radio'){
21013 cls : 'roo-hidden-value',
21014 value : this.checked ? this.inputValue : this.valueOff
21019 if (this.weight) { // Validity check?
21020 cfg.cls += " " + this.inputType + "-" + this.weight;
21023 if (this.disabled) {
21024 input.disabled=true;
21028 input.checked = this.checked;
21033 input.name = this.name;
21035 if(this.inputType != 'radio'){
21036 hidden.name = this.name;
21037 input.name = '_hidden_' + this.name;
21042 input.cls += ' input-' + this.size;
21047 ['xs','sm','md','lg'].map(function(size){
21048 if (settings[size]) {
21049 cfg.cls += ' col-' + size + '-' + settings[size];
21053 var inputblock = input;
21055 if (this.before || this.after) {
21058 cls : 'input-group',
21063 inputblock.cn.push({
21065 cls : 'input-group-addon',
21070 inputblock.cn.push(input);
21072 if(this.inputType != 'radio'){
21073 inputblock.cn.push(hidden);
21077 inputblock.cn.push({
21079 cls : 'input-group-addon',
21086 if (align ==='left' && this.fieldLabel.length) {
21087 // Roo.log("left and has label");
21092 cls : 'control-label',
21093 html : this.fieldLabel
21103 if(this.labelWidth > 12){
21104 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21107 if(this.labelWidth < 13 && this.labelmd == 0){
21108 this.labelmd = this.labelWidth;
21111 if(this.labellg > 0){
21112 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21113 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21116 if(this.labelmd > 0){
21117 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21118 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21121 if(this.labelsm > 0){
21122 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21123 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21126 if(this.labelxs > 0){
21127 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21128 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21131 } else if ( this.fieldLabel.length) {
21132 // Roo.log(" label");
21136 tag: this.boxLabel ? 'span' : 'label',
21138 cls: 'control-label box-input-label',
21139 //cls : 'input-group-addon',
21140 html : this.fieldLabel
21149 // Roo.log(" no label && no align");
21150 cfg.cn = [ inputblock ] ;
21156 var boxLabelCfg = {
21158 //'for': id, // box label is handled by onclick - so no for...
21160 html: this.boxLabel
21164 boxLabelCfg.tooltip = this.tooltip;
21167 cfg.cn.push(boxLabelCfg);
21170 if(this.inputType != 'radio'){
21171 cfg.cn.push(hidden);
21179 * return the real input element.
21181 inputEl: function ()
21183 return this.el.select('input.roo-' + this.inputType,true).first();
21185 hiddenEl: function ()
21187 return this.el.select('input.roo-hidden-value',true).first();
21190 labelEl: function()
21192 return this.el.select('label.control-label',true).first();
21194 /* depricated... */
21198 return this.labelEl();
21201 boxLabelEl: function()
21203 return this.el.select('label.box-label',true).first();
21206 initEvents : function()
21208 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21210 this.inputEl().on('click', this.onClick, this);
21212 if (this.boxLabel) {
21213 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
21216 this.startValue = this.getValue();
21219 Roo.bootstrap.CheckBox.register(this);
21223 onClick : function(e)
21225 if(this.fireEvent('click', this, e) !== false){
21226 this.setChecked(!this.checked);
21231 setChecked : function(state,suppressEvent)
21233 this.startValue = this.getValue();
21235 if(this.inputType == 'radio'){
21237 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21238 e.dom.checked = false;
21241 this.inputEl().dom.checked = true;
21243 this.inputEl().dom.value = this.inputValue;
21245 if(suppressEvent !== true){
21246 this.fireEvent('check', this, true);
21254 this.checked = state;
21256 this.inputEl().dom.checked = state;
21259 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21261 if(suppressEvent !== true){
21262 this.fireEvent('check', this, state);
21268 getValue : function()
21270 if(this.inputType == 'radio'){
21271 return this.getGroupValue();
21274 return this.hiddenEl().dom.value;
21278 getGroupValue : function()
21280 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21284 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21287 setValue : function(v,suppressEvent)
21289 if(this.inputType == 'radio'){
21290 this.setGroupValue(v, suppressEvent);
21294 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21299 setGroupValue : function(v, suppressEvent)
21301 this.startValue = this.getValue();
21303 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21304 e.dom.checked = false;
21306 if(e.dom.value == v){
21307 e.dom.checked = true;
21311 if(suppressEvent !== true){
21312 this.fireEvent('check', this, true);
21320 validate : function()
21322 if(this.getVisibilityEl().hasClass('hidden')){
21328 (this.inputType == 'radio' && this.validateRadio()) ||
21329 (this.inputType == 'checkbox' && this.validateCheckbox())
21335 this.markInvalid();
21339 validateRadio : function()
21341 if(this.getVisibilityEl().hasClass('hidden')){
21345 if(this.allowBlank){
21351 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21352 if(!e.dom.checked){
21364 validateCheckbox : function()
21367 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21368 //return (this.getValue() == this.inputValue) ? true : false;
21371 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21379 for(var i in group){
21380 if(group[i].el.isVisible(true)){
21388 for(var i in group){
21393 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21400 * Mark this field as valid
21402 markValid : function()
21406 this.fireEvent('valid', this);
21408 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21411 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21418 if(this.inputType == 'radio'){
21419 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21420 var fg = e.findParent('.form-group', false, true);
21421 if (Roo.bootstrap.version == 3) {
21422 fg.removeClass([_this.invalidClass, _this.validClass]);
21423 fg.addClass(_this.validClass);
21425 fg.removeClass(['is-valid', 'is-invalid']);
21426 fg.addClass('is-valid');
21434 var fg = this.el.findParent('.form-group', false, true);
21435 if (Roo.bootstrap.version == 3) {
21436 fg.removeClass([this.invalidClass, this.validClass]);
21437 fg.addClass(this.validClass);
21439 fg.removeClass(['is-valid', 'is-invalid']);
21440 fg.addClass('is-valid');
21445 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21451 for(var i in group){
21452 var fg = group[i].el.findParent('.form-group', false, true);
21453 if (Roo.bootstrap.version == 3) {
21454 fg.removeClass([this.invalidClass, this.validClass]);
21455 fg.addClass(this.validClass);
21457 fg.removeClass(['is-valid', 'is-invalid']);
21458 fg.addClass('is-valid');
21464 * Mark this field as invalid
21465 * @param {String} msg The validation message
21467 markInvalid : function(msg)
21469 if(this.allowBlank){
21475 this.fireEvent('invalid', this, msg);
21477 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21480 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21484 label.markInvalid();
21487 if(this.inputType == 'radio'){
21489 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21490 var fg = e.findParent('.form-group', false, true);
21491 if (Roo.bootstrap.version == 3) {
21492 fg.removeClass([_this.invalidClass, _this.validClass]);
21493 fg.addClass(_this.invalidClass);
21495 fg.removeClass(['is-invalid', 'is-valid']);
21496 fg.addClass('is-invalid');
21504 var fg = this.el.findParent('.form-group', false, true);
21505 if (Roo.bootstrap.version == 3) {
21506 fg.removeClass([_this.invalidClass, _this.validClass]);
21507 fg.addClass(_this.invalidClass);
21509 fg.removeClass(['is-invalid', 'is-valid']);
21510 fg.addClass('is-invalid');
21515 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21521 for(var i in group){
21522 var fg = group[i].el.findParent('.form-group', false, true);
21523 if (Roo.bootstrap.version == 3) {
21524 fg.removeClass([_this.invalidClass, _this.validClass]);
21525 fg.addClass(_this.invalidClass);
21527 fg.removeClass(['is-invalid', 'is-valid']);
21528 fg.addClass('is-invalid');
21534 clearInvalid : function()
21536 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21538 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21540 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21542 if (label && label.iconEl) {
21543 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21544 label.iconEl.removeClass(['is-invalid', 'is-valid']);
21548 disable : function()
21550 if(this.inputType != 'radio'){
21551 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21558 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21559 _this.getActionEl().addClass(this.disabledClass);
21560 e.dom.disabled = true;
21564 this.disabled = true;
21565 this.fireEvent("disable", this);
21569 enable : function()
21571 if(this.inputType != 'radio'){
21572 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21579 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21580 _this.getActionEl().removeClass(this.disabledClass);
21581 e.dom.disabled = false;
21585 this.disabled = false;
21586 this.fireEvent("enable", this);
21590 setBoxLabel : function(v)
21595 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21601 Roo.apply(Roo.bootstrap.CheckBox, {
21606 * register a CheckBox Group
21607 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21609 register : function(checkbox)
21611 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21612 this.groups[checkbox.groupId] = {};
21615 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21619 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21623 * fetch a CheckBox Group based on the group ID
21624 * @param {string} the group ID
21625 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21627 get: function(groupId) {
21628 if (typeof(this.groups[groupId]) == 'undefined') {
21632 return this.groups[groupId] ;
21645 * @class Roo.bootstrap.Radio
21646 * @extends Roo.bootstrap.Component
21647 * Bootstrap Radio class
21648 * @cfg {String} boxLabel - the label associated
21649 * @cfg {String} value - the value of radio
21652 * Create a new Radio
21653 * @param {Object} config The config object
21655 Roo.bootstrap.Radio = function(config){
21656 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21660 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21666 getAutoCreate : function()
21670 cls : 'form-group radio',
21675 html : this.boxLabel
21683 initEvents : function()
21685 this.parent().register(this);
21687 this.el.on('click', this.onClick, this);
21691 onClick : function(e)
21693 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21694 this.setChecked(true);
21698 setChecked : function(state, suppressEvent)
21700 this.parent().setValue(this.value, suppressEvent);
21704 setBoxLabel : function(v)
21709 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21724 * @class Roo.bootstrap.SecurePass
21725 * @extends Roo.bootstrap.Input
21726 * Bootstrap SecurePass class
21730 * Create a new SecurePass
21731 * @param {Object} config The config object
21734 Roo.bootstrap.SecurePass = function (config) {
21735 // these go here, so the translation tool can replace them..
21737 PwdEmpty: "Please type a password, and then retype it to confirm.",
21738 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21739 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21740 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21741 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21742 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21743 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21744 TooWeak: "Your password is Too Weak."
21746 this.meterLabel = "Password strength:";
21747 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21748 this.meterClass = [
21749 "roo-password-meter-tooweak",
21750 "roo-password-meter-weak",
21751 "roo-password-meter-medium",
21752 "roo-password-meter-strong",
21753 "roo-password-meter-grey"
21758 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21761 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21763 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21765 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21766 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21767 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21768 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21769 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21770 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21771 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21781 * @cfg {String/Object} Label for the strength meter (defaults to
21782 * 'Password strength:')
21787 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21788 * ['Weak', 'Medium', 'Strong'])
21791 pwdStrengths: false,
21804 initEvents: function ()
21806 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21808 if (this.el.is('input[type=password]') && Roo.isSafari) {
21809 this.el.on('keydown', this.SafariOnKeyDown, this);
21812 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21815 onRender: function (ct, position)
21817 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21818 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21819 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21821 this.trigger.createChild({
21826 cls: 'roo-password-meter-grey col-xs-12',
21829 //width: this.meterWidth + 'px'
21833 cls: 'roo-password-meter-text'
21839 if (this.hideTrigger) {
21840 this.trigger.setDisplayed(false);
21842 this.setSize(this.width || '', this.height || '');
21845 onDestroy: function ()
21847 if (this.trigger) {
21848 this.trigger.removeAllListeners();
21849 this.trigger.remove();
21852 this.wrap.remove();
21854 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21857 checkStrength: function ()
21859 var pwd = this.inputEl().getValue();
21860 if (pwd == this._lastPwd) {
21865 if (this.ClientSideStrongPassword(pwd)) {
21867 } else if (this.ClientSideMediumPassword(pwd)) {
21869 } else if (this.ClientSideWeakPassword(pwd)) {
21875 Roo.log('strength1: ' + strength);
21877 //var pm = this.trigger.child('div/div/div').dom;
21878 var pm = this.trigger.child('div/div');
21879 pm.removeClass(this.meterClass);
21880 pm.addClass(this.meterClass[strength]);
21883 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21885 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21887 this._lastPwd = pwd;
21891 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21893 this._lastPwd = '';
21895 var pm = this.trigger.child('div/div');
21896 pm.removeClass(this.meterClass);
21897 pm.addClass('roo-password-meter-grey');
21900 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21903 this.inputEl().dom.type='password';
21906 validateValue: function (value)
21909 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21912 if (value.length == 0) {
21913 if (this.allowBlank) {
21914 this.clearInvalid();
21918 this.markInvalid(this.errors.PwdEmpty);
21919 this.errorMsg = this.errors.PwdEmpty;
21927 if ('[\x21-\x7e]*'.match(value)) {
21928 this.markInvalid(this.errors.PwdBadChar);
21929 this.errorMsg = this.errors.PwdBadChar;
21932 if (value.length < 6) {
21933 this.markInvalid(this.errors.PwdShort);
21934 this.errorMsg = this.errors.PwdShort;
21937 if (value.length > 16) {
21938 this.markInvalid(this.errors.PwdLong);
21939 this.errorMsg = this.errors.PwdLong;
21943 if (this.ClientSideStrongPassword(value)) {
21945 } else if (this.ClientSideMediumPassword(value)) {
21947 } else if (this.ClientSideWeakPassword(value)) {
21954 if (strength < 2) {
21955 //this.markInvalid(this.errors.TooWeak);
21956 this.errorMsg = this.errors.TooWeak;
21961 console.log('strength2: ' + strength);
21963 //var pm = this.trigger.child('div/div/div').dom;
21965 var pm = this.trigger.child('div/div');
21966 pm.removeClass(this.meterClass);
21967 pm.addClass(this.meterClass[strength]);
21969 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21971 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21973 this.errorMsg = '';
21977 CharacterSetChecks: function (type)
21980 this.fResult = false;
21983 isctype: function (character, type)
21986 case this.kCapitalLetter:
21987 if (character >= 'A' && character <= 'Z') {
21992 case this.kSmallLetter:
21993 if (character >= 'a' && character <= 'z') {
21999 if (character >= '0' && character <= '9') {
22004 case this.kPunctuation:
22005 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
22016 IsLongEnough: function (pwd, size)
22018 return !(pwd == null || isNaN(size) || pwd.length < size);
22021 SpansEnoughCharacterSets: function (word, nb)
22023 if (!this.IsLongEnough(word, nb))
22028 var characterSetChecks = new Array(
22029 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22030 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22033 for (var index = 0; index < word.length; ++index) {
22034 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22035 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22036 characterSetChecks[nCharSet].fResult = true;
22043 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22044 if (characterSetChecks[nCharSet].fResult) {
22049 if (nCharSets < nb) {
22055 ClientSideStrongPassword: function (pwd)
22057 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22060 ClientSideMediumPassword: function (pwd)
22062 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22065 ClientSideWeakPassword: function (pwd)
22067 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22070 })//<script type="text/javascript">
22073 * Based Ext JS Library 1.1.1
22074 * Copyright(c) 2006-2007, Ext JS, LLC.
22080 * @class Roo.HtmlEditorCore
22081 * @extends Roo.Component
22082 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22084 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22087 Roo.HtmlEditorCore = function(config){
22090 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22095 * @event initialize
22096 * Fires when the editor is fully initialized (including the iframe)
22097 * @param {Roo.HtmlEditorCore} this
22102 * Fires when the editor is first receives the focus. Any insertion must wait
22103 * until after this event.
22104 * @param {Roo.HtmlEditorCore} this
22108 * @event beforesync
22109 * Fires before the textarea is updated with content from the editor iframe. Return false
22110 * to cancel the sync.
22111 * @param {Roo.HtmlEditorCore} this
22112 * @param {String} html
22116 * @event beforepush
22117 * Fires before the iframe editor is updated with content from the textarea. Return false
22118 * to cancel the push.
22119 * @param {Roo.HtmlEditorCore} this
22120 * @param {String} html
22125 * Fires when the textarea is updated with content from the editor iframe.
22126 * @param {Roo.HtmlEditorCore} this
22127 * @param {String} html
22132 * Fires when the iframe editor is updated with content from the textarea.
22133 * @param {Roo.HtmlEditorCore} this
22134 * @param {String} html
22139 * @event editorevent
22140 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22141 * @param {Roo.HtmlEditorCore} this
22147 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22149 // defaults : white / black...
22150 this.applyBlacklists();
22157 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
22161 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
22167 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22172 * @cfg {Number} height (in pixels)
22176 * @cfg {Number} width (in pixels)
22181 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22184 stylesheets: false,
22189 // private properties
22190 validationEvent : false,
22192 initialized : false,
22194 sourceEditMode : false,
22195 onFocus : Roo.emptyFn,
22197 hideMode:'offsets',
22201 // blacklist + whitelisted elements..
22208 * Protected method that will not generally be called directly. It
22209 * is called when the editor initializes the iframe with HTML contents. Override this method if you
22210 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22212 getDocMarkup : function(){
22216 // inherit styels from page...??
22217 if (this.stylesheets === false) {
22219 Roo.get(document.head).select('style').each(function(node) {
22220 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22223 Roo.get(document.head).select('link').each(function(node) {
22224 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22227 } else if (!this.stylesheets.length) {
22229 st = '<style type="text/css">' +
22230 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22233 st = '<style type="text/css">' +
22238 st += '<style type="text/css">' +
22239 'IMG { cursor: pointer } ' +
22242 var cls = 'roo-htmleditor-body';
22244 if(this.bodyCls.length){
22245 cls += ' ' + this.bodyCls;
22248 return '<html><head>' + st +
22249 //<style type="text/css">' +
22250 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22252 ' </head><body class="' + cls + '"></body></html>';
22256 onRender : function(ct, position)
22259 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22260 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22263 this.el.dom.style.border = '0 none';
22264 this.el.dom.setAttribute('tabIndex', -1);
22265 this.el.addClass('x-hidden hide');
22269 if(Roo.isIE){ // fix IE 1px bogus margin
22270 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22274 this.frameId = Roo.id();
22278 var iframe = this.owner.wrap.createChild({
22280 cls: 'form-control', // bootstrap..
22282 name: this.frameId,
22283 frameBorder : 'no',
22284 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
22289 this.iframe = iframe.dom;
22291 this.assignDocWin();
22293 this.doc.designMode = 'on';
22296 this.doc.write(this.getDocMarkup());
22300 var task = { // must defer to wait for browser to be ready
22302 //console.log("run task?" + this.doc.readyState);
22303 this.assignDocWin();
22304 if(this.doc.body || this.doc.readyState == 'complete'){
22306 this.doc.designMode="on";
22310 Roo.TaskMgr.stop(task);
22311 this.initEditor.defer(10, this);
22318 Roo.TaskMgr.start(task);
22323 onResize : function(w, h)
22325 Roo.log('resize: ' +w + ',' + h );
22326 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22330 if(typeof w == 'number'){
22332 this.iframe.style.width = w + 'px';
22334 if(typeof h == 'number'){
22336 this.iframe.style.height = h + 'px';
22338 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22345 * Toggles the editor between standard and source edit mode.
22346 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22348 toggleSourceEdit : function(sourceEditMode){
22350 this.sourceEditMode = sourceEditMode === true;
22352 if(this.sourceEditMode){
22354 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22357 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22358 //this.iframe.className = '';
22361 //this.setSize(this.owner.wrap.getSize());
22362 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22369 * Protected method that will not generally be called directly. If you need/want
22370 * custom HTML cleanup, this is the method you should override.
22371 * @param {String} html The HTML to be cleaned
22372 * return {String} The cleaned HTML
22374 cleanHtml : function(html){
22375 html = String(html);
22376 if(html.length > 5){
22377 if(Roo.isSafari){ // strip safari nonsense
22378 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22381 if(html == ' '){
22388 * HTML Editor -> Textarea
22389 * Protected method that will not generally be called directly. Syncs the contents
22390 * of the editor iframe with the textarea.
22392 syncValue : function(){
22393 if(this.initialized){
22394 var bd = (this.doc.body || this.doc.documentElement);
22395 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22396 var html = bd.innerHTML;
22398 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22399 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22401 html = '<div style="'+m[0]+'">' + html + '</div>';
22404 html = this.cleanHtml(html);
22405 // fix up the special chars.. normaly like back quotes in word...
22406 // however we do not want to do this with chinese..
22407 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22408 var cc = b.charCodeAt();
22410 (cc >= 0x4E00 && cc < 0xA000 ) ||
22411 (cc >= 0x3400 && cc < 0x4E00 ) ||
22412 (cc >= 0xf900 && cc < 0xfb00 )
22418 if(this.owner.fireEvent('beforesync', this, html) !== false){
22419 this.el.dom.value = html;
22420 this.owner.fireEvent('sync', this, html);
22426 * Protected method that will not generally be called directly. Pushes the value of the textarea
22427 * into the iframe editor.
22429 pushValue : function(){
22430 if(this.initialized){
22431 var v = this.el.dom.value.trim();
22433 // if(v.length < 1){
22437 if(this.owner.fireEvent('beforepush', this, v) !== false){
22438 var d = (this.doc.body || this.doc.documentElement);
22440 this.cleanUpPaste();
22441 this.el.dom.value = d.innerHTML;
22442 this.owner.fireEvent('push', this, v);
22448 deferFocus : function(){
22449 this.focus.defer(10, this);
22453 focus : function(){
22454 if(this.win && !this.sourceEditMode){
22461 assignDocWin: function()
22463 var iframe = this.iframe;
22466 this.doc = iframe.contentWindow.document;
22467 this.win = iframe.contentWindow;
22469 // if (!Roo.get(this.frameId)) {
22472 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22473 // this.win = Roo.get(this.frameId).dom.contentWindow;
22475 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22479 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22480 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22485 initEditor : function(){
22486 //console.log("INIT EDITOR");
22487 this.assignDocWin();
22491 this.doc.designMode="on";
22493 this.doc.write(this.getDocMarkup());
22496 var dbody = (this.doc.body || this.doc.documentElement);
22497 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22498 // this copies styles from the containing element into thsi one..
22499 // not sure why we need all of this..
22500 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22502 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22503 //ss['background-attachment'] = 'fixed'; // w3c
22504 dbody.bgProperties = 'fixed'; // ie
22505 //Roo.DomHelper.applyStyles(dbody, ss);
22506 Roo.EventManager.on(this.doc, {
22507 //'mousedown': this.onEditorEvent,
22508 'mouseup': this.onEditorEvent,
22509 'dblclick': this.onEditorEvent,
22510 'click': this.onEditorEvent,
22511 'keyup': this.onEditorEvent,
22516 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22518 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22519 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22521 this.initialized = true;
22523 this.owner.fireEvent('initialize', this);
22528 onDestroy : function(){
22534 //for (var i =0; i < this.toolbars.length;i++) {
22535 // // fixme - ask toolbars for heights?
22536 // this.toolbars[i].onDestroy();
22539 //this.wrap.dom.innerHTML = '';
22540 //this.wrap.remove();
22545 onFirstFocus : function(){
22547 this.assignDocWin();
22550 this.activated = true;
22553 if(Roo.isGecko){ // prevent silly gecko errors
22555 var s = this.win.getSelection();
22556 if(!s.focusNode || s.focusNode.nodeType != 3){
22557 var r = s.getRangeAt(0);
22558 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22563 this.execCmd('useCSS', true);
22564 this.execCmd('styleWithCSS', false);
22567 this.owner.fireEvent('activate', this);
22571 adjustFont: function(btn){
22572 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22573 //if(Roo.isSafari){ // safari
22576 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22577 if(Roo.isSafari){ // safari
22578 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22579 v = (v < 10) ? 10 : v;
22580 v = (v > 48) ? 48 : v;
22581 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22586 v = Math.max(1, v+adjust);
22588 this.execCmd('FontSize', v );
22591 onEditorEvent : function(e)
22593 this.owner.fireEvent('editorevent', this, e);
22594 // this.updateToolbar();
22595 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22598 insertTag : function(tg)
22600 // could be a bit smarter... -> wrap the current selected tRoo..
22601 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22603 range = this.createRange(this.getSelection());
22604 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22605 wrappingNode.appendChild(range.extractContents());
22606 range.insertNode(wrappingNode);
22613 this.execCmd("formatblock", tg);
22617 insertText : function(txt)
22621 var range = this.createRange();
22622 range.deleteContents();
22623 //alert(Sender.getAttribute('label'));
22625 range.insertNode(this.doc.createTextNode(txt));
22631 * Executes a Midas editor command on the editor document and performs necessary focus and
22632 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22633 * @param {String} cmd The Midas command
22634 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22636 relayCmd : function(cmd, value){
22638 this.execCmd(cmd, value);
22639 this.owner.fireEvent('editorevent', this);
22640 //this.updateToolbar();
22641 this.owner.deferFocus();
22645 * Executes a Midas editor command directly on the editor document.
22646 * For visual commands, you should use {@link #relayCmd} instead.
22647 * <b>This should only be called after the editor is initialized.</b>
22648 * @param {String} cmd The Midas command
22649 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22651 execCmd : function(cmd, value){
22652 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22659 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22661 * @param {String} text | dom node..
22663 insertAtCursor : function(text)
22666 if(!this.activated){
22672 var r = this.doc.selection.createRange();
22683 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22687 // from jquery ui (MIT licenced)
22689 var win = this.win;
22691 if (win.getSelection && win.getSelection().getRangeAt) {
22692 range = win.getSelection().getRangeAt(0);
22693 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22694 range.insertNode(node);
22695 } else if (win.document.selection && win.document.selection.createRange) {
22696 // no firefox support
22697 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22698 win.document.selection.createRange().pasteHTML(txt);
22700 // no firefox support
22701 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22702 this.execCmd('InsertHTML', txt);
22711 mozKeyPress : function(e){
22713 var c = e.getCharCode(), cmd;
22716 c = String.fromCharCode(c).toLowerCase();
22730 this.cleanUpPaste.defer(100, this);
22738 e.preventDefault();
22746 fixKeys : function(){ // load time branching for fastest keydown performance
22748 return function(e){
22749 var k = e.getKey(), r;
22752 r = this.doc.selection.createRange();
22755 r.pasteHTML('    ');
22762 r = this.doc.selection.createRange();
22764 var target = r.parentElement();
22765 if(!target || target.tagName.toLowerCase() != 'li'){
22767 r.pasteHTML('<br />');
22773 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22774 this.cleanUpPaste.defer(100, this);
22780 }else if(Roo.isOpera){
22781 return function(e){
22782 var k = e.getKey();
22786 this.execCmd('InsertHTML','    ');
22789 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22790 this.cleanUpPaste.defer(100, this);
22795 }else if(Roo.isSafari){
22796 return function(e){
22797 var k = e.getKey();
22801 this.execCmd('InsertText','\t');
22805 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22806 this.cleanUpPaste.defer(100, this);
22814 getAllAncestors: function()
22816 var p = this.getSelectedNode();
22819 a.push(p); // push blank onto stack..
22820 p = this.getParentElement();
22824 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22828 a.push(this.doc.body);
22832 lastSelNode : false,
22835 getSelection : function()
22837 this.assignDocWin();
22838 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22841 getSelectedNode: function()
22843 // this may only work on Gecko!!!
22845 // should we cache this!!!!
22850 var range = this.createRange(this.getSelection()).cloneRange();
22853 var parent = range.parentElement();
22855 var testRange = range.duplicate();
22856 testRange.moveToElementText(parent);
22857 if (testRange.inRange(range)) {
22860 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22863 parent = parent.parentElement;
22868 // is ancestor a text element.
22869 var ac = range.commonAncestorContainer;
22870 if (ac.nodeType == 3) {
22871 ac = ac.parentNode;
22874 var ar = ac.childNodes;
22877 var other_nodes = [];
22878 var has_other_nodes = false;
22879 for (var i=0;i<ar.length;i++) {
22880 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22883 // fullly contained node.
22885 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22890 // probably selected..
22891 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22892 other_nodes.push(ar[i]);
22896 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22901 has_other_nodes = true;
22903 if (!nodes.length && other_nodes.length) {
22904 nodes= other_nodes;
22906 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22912 createRange: function(sel)
22914 // this has strange effects when using with
22915 // top toolbar - not sure if it's a great idea.
22916 //this.editor.contentWindow.focus();
22917 if (typeof sel != "undefined") {
22919 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22921 return this.doc.createRange();
22924 return this.doc.createRange();
22927 getParentElement: function()
22930 this.assignDocWin();
22931 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22933 var range = this.createRange(sel);
22936 var p = range.commonAncestorContainer;
22937 while (p.nodeType == 3) { // text node
22948 * Range intersection.. the hard stuff...
22952 * [ -- selected range --- ]
22956 * if end is before start or hits it. fail.
22957 * if start is after end or hits it fail.
22959 * if either hits (but other is outside. - then it's not
22965 // @see http://www.thismuchiknow.co.uk/?p=64.
22966 rangeIntersectsNode : function(range, node)
22968 var nodeRange = node.ownerDocument.createRange();
22970 nodeRange.selectNode(node);
22972 nodeRange.selectNodeContents(node);
22975 var rangeStartRange = range.cloneRange();
22976 rangeStartRange.collapse(true);
22978 var rangeEndRange = range.cloneRange();
22979 rangeEndRange.collapse(false);
22981 var nodeStartRange = nodeRange.cloneRange();
22982 nodeStartRange.collapse(true);
22984 var nodeEndRange = nodeRange.cloneRange();
22985 nodeEndRange.collapse(false);
22987 return rangeStartRange.compareBoundaryPoints(
22988 Range.START_TO_START, nodeEndRange) == -1 &&
22989 rangeEndRange.compareBoundaryPoints(
22990 Range.START_TO_START, nodeStartRange) == 1;
22994 rangeCompareNode : function(range, node)
22996 var nodeRange = node.ownerDocument.createRange();
22998 nodeRange.selectNode(node);
23000 nodeRange.selectNodeContents(node);
23004 range.collapse(true);
23006 nodeRange.collapse(true);
23008 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23009 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
23011 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23013 var nodeIsBefore = ss == 1;
23014 var nodeIsAfter = ee == -1;
23016 if (nodeIsBefore && nodeIsAfter) {
23019 if (!nodeIsBefore && nodeIsAfter) {
23020 return 1; //right trailed.
23023 if (nodeIsBefore && !nodeIsAfter) {
23024 return 2; // left trailed.
23030 // private? - in a new class?
23031 cleanUpPaste : function()
23033 // cleans up the whole document..
23034 Roo.log('cleanuppaste');
23036 this.cleanUpChildren(this.doc.body);
23037 var clean = this.cleanWordChars(this.doc.body.innerHTML);
23038 if (clean != this.doc.body.innerHTML) {
23039 this.doc.body.innerHTML = clean;
23044 cleanWordChars : function(input) {// change the chars to hex code
23045 var he = Roo.HtmlEditorCore;
23047 var output = input;
23048 Roo.each(he.swapCodes, function(sw) {
23049 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23051 output = output.replace(swapper, sw[1]);
23058 cleanUpChildren : function (n)
23060 if (!n.childNodes.length) {
23063 for (var i = n.childNodes.length-1; i > -1 ; i--) {
23064 this.cleanUpChild(n.childNodes[i]);
23071 cleanUpChild : function (node)
23074 //console.log(node);
23075 if (node.nodeName == "#text") {
23076 // clean up silly Windows -- stuff?
23079 if (node.nodeName == "#comment") {
23080 node.parentNode.removeChild(node);
23081 // clean up silly Windows -- stuff?
23084 var lcname = node.tagName.toLowerCase();
23085 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23086 // whitelist of tags..
23088 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23090 node.parentNode.removeChild(node);
23095 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23097 // remove <a name=....> as rendering on yahoo mailer is borked with this.
23098 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23100 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23101 // remove_keep_children = true;
23104 if (remove_keep_children) {
23105 this.cleanUpChildren(node);
23106 // inserts everything just before this node...
23107 while (node.childNodes.length) {
23108 var cn = node.childNodes[0];
23109 node.removeChild(cn);
23110 node.parentNode.insertBefore(cn, node);
23112 node.parentNode.removeChild(node);
23116 if (!node.attributes || !node.attributes.length) {
23117 this.cleanUpChildren(node);
23121 function cleanAttr(n,v)
23124 if (v.match(/^\./) || v.match(/^\//)) {
23127 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23130 if (v.match(/^#/)) {
23133 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23134 node.removeAttribute(n);
23138 var cwhite = this.cwhite;
23139 var cblack = this.cblack;
23141 function cleanStyle(n,v)
23143 if (v.match(/expression/)) { //XSS?? should we even bother..
23144 node.removeAttribute(n);
23148 var parts = v.split(/;/);
23151 Roo.each(parts, function(p) {
23152 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23156 var l = p.split(':').shift().replace(/\s+/g,'');
23157 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23159 if ( cwhite.length && cblack.indexOf(l) > -1) {
23160 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23161 //node.removeAttribute(n);
23165 // only allow 'c whitelisted system attributes'
23166 if ( cwhite.length && cwhite.indexOf(l) < 0) {
23167 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23168 //node.removeAttribute(n);
23178 if (clean.length) {
23179 node.setAttribute(n, clean.join(';'));
23181 node.removeAttribute(n);
23187 for (var i = node.attributes.length-1; i > -1 ; i--) {
23188 var a = node.attributes[i];
23191 if (a.name.toLowerCase().substr(0,2)=='on') {
23192 node.removeAttribute(a.name);
23195 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23196 node.removeAttribute(a.name);
23199 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23200 cleanAttr(a.name,a.value); // fixme..
23203 if (a.name == 'style') {
23204 cleanStyle(a.name,a.value);
23207 /// clean up MS crap..
23208 // tecnically this should be a list of valid class'es..
23211 if (a.name == 'class') {
23212 if (a.value.match(/^Mso/)) {
23213 node.className = '';
23216 if (a.value.match(/^body$/)) {
23217 node.className = '';
23228 this.cleanUpChildren(node);
23234 * Clean up MS wordisms...
23236 cleanWord : function(node)
23241 this.cleanWord(this.doc.body);
23244 if (node.nodeName == "#text") {
23245 // clean up silly Windows -- stuff?
23248 if (node.nodeName == "#comment") {
23249 node.parentNode.removeChild(node);
23250 // clean up silly Windows -- stuff?
23254 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23255 node.parentNode.removeChild(node);
23259 // remove - but keep children..
23260 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23261 while (node.childNodes.length) {
23262 var cn = node.childNodes[0];
23263 node.removeChild(cn);
23264 node.parentNode.insertBefore(cn, node);
23266 node.parentNode.removeChild(node);
23267 this.iterateChildren(node, this.cleanWord);
23271 if (node.className.length) {
23273 var cn = node.className.split(/\W+/);
23275 Roo.each(cn, function(cls) {
23276 if (cls.match(/Mso[a-zA-Z]+/)) {
23281 node.className = cna.length ? cna.join(' ') : '';
23283 node.removeAttribute("class");
23287 if (node.hasAttribute("lang")) {
23288 node.removeAttribute("lang");
23291 if (node.hasAttribute("style")) {
23293 var styles = node.getAttribute("style").split(";");
23295 Roo.each(styles, function(s) {
23296 if (!s.match(/:/)) {
23299 var kv = s.split(":");
23300 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23303 // what ever is left... we allow.
23306 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23307 if (!nstyle.length) {
23308 node.removeAttribute('style');
23311 this.iterateChildren(node, this.cleanWord);
23317 * iterateChildren of a Node, calling fn each time, using this as the scole..
23318 * @param {DomNode} node node to iterate children of.
23319 * @param {Function} fn method of this class to call on each item.
23321 iterateChildren : function(node, fn)
23323 if (!node.childNodes.length) {
23326 for (var i = node.childNodes.length-1; i > -1 ; i--) {
23327 fn.call(this, node.childNodes[i])
23333 * cleanTableWidths.
23335 * Quite often pasting from word etc.. results in tables with column and widths.
23336 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23339 cleanTableWidths : function(node)
23344 this.cleanTableWidths(this.doc.body);
23349 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23352 Roo.log(node.tagName);
23353 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23354 this.iterateChildren(node, this.cleanTableWidths);
23357 if (node.hasAttribute('width')) {
23358 node.removeAttribute('width');
23362 if (node.hasAttribute("style")) {
23365 var styles = node.getAttribute("style").split(";");
23367 Roo.each(styles, function(s) {
23368 if (!s.match(/:/)) {
23371 var kv = s.split(":");
23372 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23375 // what ever is left... we allow.
23378 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23379 if (!nstyle.length) {
23380 node.removeAttribute('style');
23384 this.iterateChildren(node, this.cleanTableWidths);
23392 domToHTML : function(currentElement, depth, nopadtext) {
23394 depth = depth || 0;
23395 nopadtext = nopadtext || false;
23397 if (!currentElement) {
23398 return this.domToHTML(this.doc.body);
23401 //Roo.log(currentElement);
23403 var allText = false;
23404 var nodeName = currentElement.nodeName;
23405 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23407 if (nodeName == '#text') {
23409 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23414 if (nodeName != 'BODY') {
23417 // Prints the node tagName, such as <A>, <IMG>, etc
23420 for(i = 0; i < currentElement.attributes.length;i++) {
23422 var aname = currentElement.attributes.item(i).name;
23423 if (!currentElement.attributes.item(i).value.length) {
23426 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23429 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23438 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23441 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23446 // Traverse the tree
23448 var currentElementChild = currentElement.childNodes.item(i);
23449 var allText = true;
23450 var innerHTML = '';
23452 while (currentElementChild) {
23453 // Formatting code (indent the tree so it looks nice on the screen)
23454 var nopad = nopadtext;
23455 if (lastnode == 'SPAN') {
23459 if (currentElementChild.nodeName == '#text') {
23460 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23461 toadd = nopadtext ? toadd : toadd.trim();
23462 if (!nopad && toadd.length > 80) {
23463 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23465 innerHTML += toadd;
23468 currentElementChild = currentElement.childNodes.item(i);
23474 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23476 // Recursively traverse the tree structure of the child node
23477 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23478 lastnode = currentElementChild.nodeName;
23480 currentElementChild=currentElement.childNodes.item(i);
23486 // The remaining code is mostly for formatting the tree
23487 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23492 ret+= "</"+tagName+">";
23498 applyBlacklists : function()
23500 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23501 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23505 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23506 if (b.indexOf(tag) > -1) {
23509 this.white.push(tag);
23513 Roo.each(w, function(tag) {
23514 if (b.indexOf(tag) > -1) {
23517 if (this.white.indexOf(tag) > -1) {
23520 this.white.push(tag);
23525 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23526 if (w.indexOf(tag) > -1) {
23529 this.black.push(tag);
23533 Roo.each(b, function(tag) {
23534 if (w.indexOf(tag) > -1) {
23537 if (this.black.indexOf(tag) > -1) {
23540 this.black.push(tag);
23545 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23546 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23550 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23551 if (b.indexOf(tag) > -1) {
23554 this.cwhite.push(tag);
23558 Roo.each(w, function(tag) {
23559 if (b.indexOf(tag) > -1) {
23562 if (this.cwhite.indexOf(tag) > -1) {
23565 this.cwhite.push(tag);
23570 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23571 if (w.indexOf(tag) > -1) {
23574 this.cblack.push(tag);
23578 Roo.each(b, function(tag) {
23579 if (w.indexOf(tag) > -1) {
23582 if (this.cblack.indexOf(tag) > -1) {
23585 this.cblack.push(tag);
23590 setStylesheets : function(stylesheets)
23592 if(typeof(stylesheets) == 'string'){
23593 Roo.get(this.iframe.contentDocument.head).createChild({
23595 rel : 'stylesheet',
23604 Roo.each(stylesheets, function(s) {
23609 Roo.get(_this.iframe.contentDocument.head).createChild({
23611 rel : 'stylesheet',
23620 removeStylesheets : function()
23624 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23629 setStyle : function(style)
23631 Roo.get(this.iframe.contentDocument.head).createChild({
23640 // hide stuff that is not compatible
23654 * @event specialkey
23658 * @cfg {String} fieldClass @hide
23661 * @cfg {String} focusClass @hide
23664 * @cfg {String} autoCreate @hide
23667 * @cfg {String} inputType @hide
23670 * @cfg {String} invalidClass @hide
23673 * @cfg {String} invalidText @hide
23676 * @cfg {String} msgFx @hide
23679 * @cfg {String} validateOnBlur @hide
23683 Roo.HtmlEditorCore.white = [
23684 'area', 'br', 'img', 'input', 'hr', 'wbr',
23686 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23687 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23688 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23689 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23690 'table', 'ul', 'xmp',
23692 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23695 'dir', 'menu', 'ol', 'ul', 'dl',
23701 Roo.HtmlEditorCore.black = [
23702 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23704 'base', 'basefont', 'bgsound', 'blink', 'body',
23705 'frame', 'frameset', 'head', 'html', 'ilayer',
23706 'iframe', 'layer', 'link', 'meta', 'object',
23707 'script', 'style' ,'title', 'xml' // clean later..
23709 Roo.HtmlEditorCore.clean = [
23710 'script', 'style', 'title', 'xml'
23712 Roo.HtmlEditorCore.remove = [
23717 Roo.HtmlEditorCore.ablack = [
23721 Roo.HtmlEditorCore.aclean = [
23722 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23726 Roo.HtmlEditorCore.pwhite= [
23727 'http', 'https', 'mailto'
23730 // white listed style attributes.
23731 Roo.HtmlEditorCore.cwhite= [
23732 // 'text-align', /// default is to allow most things..
23738 // black listed style attributes.
23739 Roo.HtmlEditorCore.cblack= [
23740 // 'font-size' -- this can be set by the project
23744 Roo.HtmlEditorCore.swapCodes =[
23763 * @class Roo.bootstrap.HtmlEditor
23764 * @extends Roo.bootstrap.TextArea
23765 * Bootstrap HtmlEditor class
23768 * Create a new HtmlEditor
23769 * @param {Object} config The config object
23772 Roo.bootstrap.HtmlEditor = function(config){
23773 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23774 if (!this.toolbars) {
23775 this.toolbars = [];
23778 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23781 * @event initialize
23782 * Fires when the editor is fully initialized (including the iframe)
23783 * @param {HtmlEditor} this
23788 * Fires when the editor is first receives the focus. Any insertion must wait
23789 * until after this event.
23790 * @param {HtmlEditor} this
23794 * @event beforesync
23795 * Fires before the textarea is updated with content from the editor iframe. Return false
23796 * to cancel the sync.
23797 * @param {HtmlEditor} this
23798 * @param {String} html
23802 * @event beforepush
23803 * Fires before the iframe editor is updated with content from the textarea. Return false
23804 * to cancel the push.
23805 * @param {HtmlEditor} this
23806 * @param {String} html
23811 * Fires when the textarea is updated with content from the editor iframe.
23812 * @param {HtmlEditor} this
23813 * @param {String} html
23818 * Fires when the iframe editor is updated with content from the textarea.
23819 * @param {HtmlEditor} this
23820 * @param {String} html
23824 * @event editmodechange
23825 * Fires when the editor switches edit modes
23826 * @param {HtmlEditor} this
23827 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23829 editmodechange: true,
23831 * @event editorevent
23832 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23833 * @param {HtmlEditor} this
23837 * @event firstfocus
23838 * Fires when on first focus - needed by toolbars..
23839 * @param {HtmlEditor} this
23844 * Auto save the htmlEditor value as a file into Events
23845 * @param {HtmlEditor} this
23849 * @event savedpreview
23850 * preview the saved version of htmlEditor
23851 * @param {HtmlEditor} this
23858 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23862 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23867 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23872 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23877 * @cfg {Number} height (in pixels)
23881 * @cfg {Number} width (in pixels)
23886 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23889 stylesheets: false,
23894 // private properties
23895 validationEvent : false,
23897 initialized : false,
23900 onFocus : Roo.emptyFn,
23902 hideMode:'offsets',
23904 tbContainer : false,
23908 toolbarContainer :function() {
23909 return this.wrap.select('.x-html-editor-tb',true).first();
23913 * Protected method that will not generally be called directly. It
23914 * is called when the editor creates its toolbar. Override this method if you need to
23915 * add custom toolbar buttons.
23916 * @param {HtmlEditor} editor
23918 createToolbar : function(){
23919 Roo.log('renewing');
23920 Roo.log("create toolbars");
23922 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23923 this.toolbars[0].render(this.toolbarContainer());
23927 // if (!editor.toolbars || !editor.toolbars.length) {
23928 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23931 // for (var i =0 ; i < editor.toolbars.length;i++) {
23932 // editor.toolbars[i] = Roo.factory(
23933 // typeof(editor.toolbars[i]) == 'string' ?
23934 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23935 // Roo.bootstrap.HtmlEditor);
23936 // editor.toolbars[i].init(editor);
23942 onRender : function(ct, position)
23944 // Roo.log("Call onRender: " + this.xtype);
23946 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23948 this.wrap = this.inputEl().wrap({
23949 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23952 this.editorcore.onRender(ct, position);
23954 if (this.resizable) {
23955 this.resizeEl = new Roo.Resizable(this.wrap, {
23959 minHeight : this.height,
23960 height: this.height,
23961 handles : this.resizable,
23964 resize : function(r, w, h) {
23965 _t.onResize(w,h); // -something
23971 this.createToolbar(this);
23974 if(!this.width && this.resizable){
23975 this.setSize(this.wrap.getSize());
23977 if (this.resizeEl) {
23978 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23979 // should trigger onReize..
23985 onResize : function(w, h)
23987 Roo.log('resize: ' +w + ',' + h );
23988 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23992 if(this.inputEl() ){
23993 if(typeof w == 'number'){
23994 var aw = w - this.wrap.getFrameWidth('lr');
23995 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23998 if(typeof h == 'number'){
23999 var tbh = -11; // fixme it needs to tool bar size!
24000 for (var i =0; i < this.toolbars.length;i++) {
24001 // fixme - ask toolbars for heights?
24002 tbh += this.toolbars[i].el.getHeight();
24003 //if (this.toolbars[i].footer) {
24004 // tbh += this.toolbars[i].footer.el.getHeight();
24012 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24013 ah -= 5; // knock a few pixes off for look..
24014 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24018 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24019 this.editorcore.onResize(ew,eh);
24024 * Toggles the editor between standard and source edit mode.
24025 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24027 toggleSourceEdit : function(sourceEditMode)
24029 this.editorcore.toggleSourceEdit(sourceEditMode);
24031 if(this.editorcore.sourceEditMode){
24032 Roo.log('editor - showing textarea');
24035 // Roo.log(this.syncValue());
24037 this.inputEl().removeClass(['hide', 'x-hidden']);
24038 this.inputEl().dom.removeAttribute('tabIndex');
24039 this.inputEl().focus();
24041 Roo.log('editor - hiding textarea');
24043 // Roo.log(this.pushValue());
24046 this.inputEl().addClass(['hide', 'x-hidden']);
24047 this.inputEl().dom.setAttribute('tabIndex', -1);
24048 //this.deferFocus();
24051 if(this.resizable){
24052 this.setSize(this.wrap.getSize());
24055 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24058 // private (for BoxComponent)
24059 adjustSize : Roo.BoxComponent.prototype.adjustSize,
24061 // private (for BoxComponent)
24062 getResizeEl : function(){
24066 // private (for BoxComponent)
24067 getPositionEl : function(){
24072 initEvents : function(){
24073 this.originalValue = this.getValue();
24077 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24080 // markInvalid : Roo.emptyFn,
24082 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24085 // clearInvalid : Roo.emptyFn,
24087 setValue : function(v){
24088 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24089 this.editorcore.pushValue();
24094 deferFocus : function(){
24095 this.focus.defer(10, this);
24099 focus : function(){
24100 this.editorcore.focus();
24106 onDestroy : function(){
24112 for (var i =0; i < this.toolbars.length;i++) {
24113 // fixme - ask toolbars for heights?
24114 this.toolbars[i].onDestroy();
24117 this.wrap.dom.innerHTML = '';
24118 this.wrap.remove();
24123 onFirstFocus : function(){
24124 //Roo.log("onFirstFocus");
24125 this.editorcore.onFirstFocus();
24126 for (var i =0; i < this.toolbars.length;i++) {
24127 this.toolbars[i].onFirstFocus();
24133 syncValue : function()
24135 this.editorcore.syncValue();
24138 pushValue : function()
24140 this.editorcore.pushValue();
24144 // hide stuff that is not compatible
24158 * @event specialkey
24162 * @cfg {String} fieldClass @hide
24165 * @cfg {String} focusClass @hide
24168 * @cfg {String} autoCreate @hide
24171 * @cfg {String} inputType @hide
24175 * @cfg {String} invalidText @hide
24178 * @cfg {String} msgFx @hide
24181 * @cfg {String} validateOnBlur @hide
24190 Roo.namespace('Roo.bootstrap.htmleditor');
24192 * @class Roo.bootstrap.HtmlEditorToolbar1
24198 new Roo.bootstrap.HtmlEditor({
24201 new Roo.bootstrap.HtmlEditorToolbar1({
24202 disable : { fonts: 1 , format: 1, ..., ... , ...],
24208 * @cfg {Object} disable List of elements to disable..
24209 * @cfg {Array} btns List of additional buttons.
24213 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24216 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24219 Roo.apply(this, config);
24221 // default disabled, based on 'good practice'..
24222 this.disable = this.disable || {};
24223 Roo.applyIf(this.disable, {
24226 specialElements : true
24228 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24230 this.editor = config.editor;
24231 this.editorcore = config.editor.editorcore;
24233 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24235 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24236 // dont call parent... till later.
24238 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
24243 editorcore : false,
24248 "h1","h2","h3","h4","h5","h6",
24250 "abbr", "acronym", "address", "cite", "samp", "var",
24254 onRender : function(ct, position)
24256 // Roo.log("Call onRender: " + this.xtype);
24258 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24260 this.el.dom.style.marginBottom = '0';
24262 var editorcore = this.editorcore;
24263 var editor= this.editor;
24266 var btn = function(id,cmd , toggle, handler, html){
24268 var event = toggle ? 'toggle' : 'click';
24273 xns: Roo.bootstrap,
24277 enableToggle:toggle !== false,
24279 pressed : toggle ? false : null,
24282 a.listeners[toggle ? 'toggle' : 'click'] = function() {
24283 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
24289 // var cb_box = function...
24294 xns: Roo.bootstrap,
24299 xns: Roo.bootstrap,
24303 Roo.each(this.formats, function(f) {
24304 style.menu.items.push({
24306 xns: Roo.bootstrap,
24307 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24312 editorcore.insertTag(this.tagname);
24319 children.push(style);
24321 btn('bold',false,true);
24322 btn('italic',false,true);
24323 btn('align-left', 'justifyleft',true);
24324 btn('align-center', 'justifycenter',true);
24325 btn('align-right' , 'justifyright',true);
24326 btn('link', false, false, function(btn) {
24327 //Roo.log("create link?");
24328 var url = prompt(this.createLinkText, this.defaultLinkValue);
24329 if(url && url != 'http:/'+'/'){
24330 this.editorcore.relayCmd('createlink', url);
24333 btn('list','insertunorderedlist',true);
24334 btn('pencil', false,true, function(btn){
24336 this.toggleSourceEdit(btn.pressed);
24339 if (this.editor.btns.length > 0) {
24340 for (var i = 0; i<this.editor.btns.length; i++) {
24341 children.push(this.editor.btns[i]);
24349 xns: Roo.bootstrap,
24354 xns: Roo.bootstrap,
24359 cog.menu.items.push({
24361 xns: Roo.bootstrap,
24362 html : Clean styles,
24367 editorcore.insertTag(this.tagname);
24376 this.xtype = 'NavSimplebar';
24378 for(var i=0;i< children.length;i++) {
24380 this.buttons.add(this.addxtypeChild(children[i]));
24384 editor.on('editorevent', this.updateToolbar, this);
24386 onBtnClick : function(id)
24388 this.editorcore.relayCmd(id);
24389 this.editorcore.focus();
24393 * Protected method that will not generally be called directly. It triggers
24394 * a toolbar update by reading the markup state of the current selection in the editor.
24396 updateToolbar: function(){
24398 if(!this.editorcore.activated){
24399 this.editor.onFirstFocus(); // is this neeed?
24403 var btns = this.buttons;
24404 var doc = this.editorcore.doc;
24405 btns.get('bold').setActive(doc.queryCommandState('bold'));
24406 btns.get('italic').setActive(doc.queryCommandState('italic'));
24407 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24409 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24410 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24411 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24413 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24414 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24417 var ans = this.editorcore.getAllAncestors();
24418 if (this.formatCombo) {
24421 var store = this.formatCombo.store;
24422 this.formatCombo.setValue("");
24423 for (var i =0; i < ans.length;i++) {
24424 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24426 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24434 // hides menus... - so this cant be on a menu...
24435 Roo.bootstrap.MenuMgr.hideAll();
24437 Roo.bootstrap.MenuMgr.hideAll();
24438 //this.editorsyncValue();
24440 onFirstFocus: function() {
24441 this.buttons.each(function(item){
24445 toggleSourceEdit : function(sourceEditMode){
24448 if(sourceEditMode){
24449 Roo.log("disabling buttons");
24450 this.buttons.each( function(item){
24451 if(item.cmd != 'pencil'){
24457 Roo.log("enabling buttons");
24458 if(this.editorcore.initialized){
24459 this.buttons.each( function(item){
24465 Roo.log("calling toggole on editor");
24466 // tell the editor that it's been pressed..
24467 this.editor.toggleSourceEdit(sourceEditMode);
24477 * @class Roo.bootstrap.Table.AbstractSelectionModel
24478 * @extends Roo.util.Observable
24479 * Abstract base class for grid SelectionModels. It provides the interface that should be
24480 * implemented by descendant classes. This class should not be directly instantiated.
24483 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24484 this.locked = false;
24485 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24489 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24490 /** @ignore Called by the grid automatically. Do not call directly. */
24491 init : function(grid){
24497 * Locks the selections.
24500 this.locked = true;
24504 * Unlocks the selections.
24506 unlock : function(){
24507 this.locked = false;
24511 * Returns true if the selections are locked.
24512 * @return {Boolean}
24514 isLocked : function(){
24515 return this.locked;
24519 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24520 * @class Roo.bootstrap.Table.RowSelectionModel
24521 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24522 * It supports multiple selections and keyboard selection/navigation.
24524 * @param {Object} config
24527 Roo.bootstrap.Table.RowSelectionModel = function(config){
24528 Roo.apply(this, config);
24529 this.selections = new Roo.util.MixedCollection(false, function(o){
24534 this.lastActive = false;
24538 * @event selectionchange
24539 * Fires when the selection changes
24540 * @param {SelectionModel} this
24542 "selectionchange" : true,
24544 * @event afterselectionchange
24545 * Fires after the selection changes (eg. by key press or clicking)
24546 * @param {SelectionModel} this
24548 "afterselectionchange" : true,
24550 * @event beforerowselect
24551 * Fires when a row is selected being selected, return false to cancel.
24552 * @param {SelectionModel} this
24553 * @param {Number} rowIndex The selected index
24554 * @param {Boolean} keepExisting False if other selections will be cleared
24556 "beforerowselect" : true,
24559 * Fires when a row is selected.
24560 * @param {SelectionModel} this
24561 * @param {Number} rowIndex The selected index
24562 * @param {Roo.data.Record} r The record
24564 "rowselect" : true,
24566 * @event rowdeselect
24567 * Fires when a row is deselected.
24568 * @param {SelectionModel} this
24569 * @param {Number} rowIndex The selected index
24571 "rowdeselect" : true
24573 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24574 this.locked = false;
24577 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24579 * @cfg {Boolean} singleSelect
24580 * True to allow selection of only one row at a time (defaults to false)
24582 singleSelect : false,
24585 initEvents : function()
24588 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24589 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24590 //}else{ // allow click to work like normal
24591 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24593 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24594 this.grid.on("rowclick", this.handleMouseDown, this);
24596 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24597 "up" : function(e){
24599 this.selectPrevious(e.shiftKey);
24600 }else if(this.last !== false && this.lastActive !== false){
24601 var last = this.last;
24602 this.selectRange(this.last, this.lastActive-1);
24603 this.grid.getView().focusRow(this.lastActive);
24604 if(last !== false){
24608 this.selectFirstRow();
24610 this.fireEvent("afterselectionchange", this);
24612 "down" : function(e){
24614 this.selectNext(e.shiftKey);
24615 }else if(this.last !== false && this.lastActive !== false){
24616 var last = this.last;
24617 this.selectRange(this.last, this.lastActive+1);
24618 this.grid.getView().focusRow(this.lastActive);
24619 if(last !== false){
24623 this.selectFirstRow();
24625 this.fireEvent("afterselectionchange", this);
24629 this.grid.store.on('load', function(){
24630 this.selections.clear();
24633 var view = this.grid.view;
24634 view.on("refresh", this.onRefresh, this);
24635 view.on("rowupdated", this.onRowUpdated, this);
24636 view.on("rowremoved", this.onRemove, this);
24641 onRefresh : function()
24643 var ds = this.grid.store, i, v = this.grid.view;
24644 var s = this.selections;
24645 s.each(function(r){
24646 if((i = ds.indexOfId(r.id)) != -1){
24655 onRemove : function(v, index, r){
24656 this.selections.remove(r);
24660 onRowUpdated : function(v, index, r){
24661 if(this.isSelected(r)){
24662 v.onRowSelect(index);
24668 * @param {Array} records The records to select
24669 * @param {Boolean} keepExisting (optional) True to keep existing selections
24671 selectRecords : function(records, keepExisting)
24674 this.clearSelections();
24676 var ds = this.grid.store;
24677 for(var i = 0, len = records.length; i < len; i++){
24678 this.selectRow(ds.indexOf(records[i]), true);
24683 * Gets the number of selected rows.
24686 getCount : function(){
24687 return this.selections.length;
24691 * Selects the first row in the grid.
24693 selectFirstRow : function(){
24698 * Select the last row.
24699 * @param {Boolean} keepExisting (optional) True to keep existing selections
24701 selectLastRow : function(keepExisting){
24702 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24703 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24707 * Selects the row immediately following the last selected row.
24708 * @param {Boolean} keepExisting (optional) True to keep existing selections
24710 selectNext : function(keepExisting)
24712 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24713 this.selectRow(this.last+1, keepExisting);
24714 this.grid.getView().focusRow(this.last);
24719 * Selects the row that precedes the last selected row.
24720 * @param {Boolean} keepExisting (optional) True to keep existing selections
24722 selectPrevious : function(keepExisting){
24724 this.selectRow(this.last-1, keepExisting);
24725 this.grid.getView().focusRow(this.last);
24730 * Returns the selected records
24731 * @return {Array} Array of selected records
24733 getSelections : function(){
24734 return [].concat(this.selections.items);
24738 * Returns the first selected record.
24741 getSelected : function(){
24742 return this.selections.itemAt(0);
24747 * Clears all selections.
24749 clearSelections : function(fast)
24755 var ds = this.grid.store;
24756 var s = this.selections;
24757 s.each(function(r){
24758 this.deselectRow(ds.indexOfId(r.id));
24762 this.selections.clear();
24769 * Selects all rows.
24771 selectAll : function(){
24775 this.selections.clear();
24776 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24777 this.selectRow(i, true);
24782 * Returns True if there is a selection.
24783 * @return {Boolean}
24785 hasSelection : function(){
24786 return this.selections.length > 0;
24790 * Returns True if the specified row is selected.
24791 * @param {Number/Record} record The record or index of the record to check
24792 * @return {Boolean}
24794 isSelected : function(index){
24795 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24796 return (r && this.selections.key(r.id) ? true : false);
24800 * Returns True if the specified record id is selected.
24801 * @param {String} id The id of record to check
24802 * @return {Boolean}
24804 isIdSelected : function(id){
24805 return (this.selections.key(id) ? true : false);
24810 handleMouseDBClick : function(e, t){
24814 handleMouseDown : function(e, t)
24816 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24817 if(this.isLocked() || rowIndex < 0 ){
24820 if(e.shiftKey && this.last !== false){
24821 var last = this.last;
24822 this.selectRange(last, rowIndex, e.ctrlKey);
24823 this.last = last; // reset the last
24827 var isSelected = this.isSelected(rowIndex);
24828 //Roo.log("select row:" + rowIndex);
24830 this.deselectRow(rowIndex);
24832 this.selectRow(rowIndex, true);
24836 if(e.button !== 0 && isSelected){
24837 alert('rowIndex 2: ' + rowIndex);
24838 view.focusRow(rowIndex);
24839 }else if(e.ctrlKey && isSelected){
24840 this.deselectRow(rowIndex);
24841 }else if(!isSelected){
24842 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24843 view.focusRow(rowIndex);
24847 this.fireEvent("afterselectionchange", this);
24850 handleDragableRowClick : function(grid, rowIndex, e)
24852 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24853 this.selectRow(rowIndex, false);
24854 grid.view.focusRow(rowIndex);
24855 this.fireEvent("afterselectionchange", this);
24860 * Selects multiple rows.
24861 * @param {Array} rows Array of the indexes of the row to select
24862 * @param {Boolean} keepExisting (optional) True to keep existing selections
24864 selectRows : function(rows, keepExisting){
24866 this.clearSelections();
24868 for(var i = 0, len = rows.length; i < len; i++){
24869 this.selectRow(rows[i], true);
24874 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24875 * @param {Number} startRow The index of the first row in the range
24876 * @param {Number} endRow The index of the last row in the range
24877 * @param {Boolean} keepExisting (optional) True to retain existing selections
24879 selectRange : function(startRow, endRow, keepExisting){
24884 this.clearSelections();
24886 if(startRow <= endRow){
24887 for(var i = startRow; i <= endRow; i++){
24888 this.selectRow(i, true);
24891 for(var i = startRow; i >= endRow; i--){
24892 this.selectRow(i, true);
24898 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24899 * @param {Number} startRow The index of the first row in the range
24900 * @param {Number} endRow The index of the last row in the range
24902 deselectRange : function(startRow, endRow, preventViewNotify){
24906 for(var i = startRow; i <= endRow; i++){
24907 this.deselectRow(i, preventViewNotify);
24913 * @param {Number} row The index of the row to select
24914 * @param {Boolean} keepExisting (optional) True to keep existing selections
24916 selectRow : function(index, keepExisting, preventViewNotify)
24918 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24921 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24922 if(!keepExisting || this.singleSelect){
24923 this.clearSelections();
24926 var r = this.grid.store.getAt(index);
24927 //console.log('selectRow - record id :' + r.id);
24929 this.selections.add(r);
24930 this.last = this.lastActive = index;
24931 if(!preventViewNotify){
24932 var proxy = new Roo.Element(
24933 this.grid.getRowDom(index)
24935 proxy.addClass('bg-info info');
24937 this.fireEvent("rowselect", this, index, r);
24938 this.fireEvent("selectionchange", this);
24944 * @param {Number} row The index of the row to deselect
24946 deselectRow : function(index, preventViewNotify)
24951 if(this.last == index){
24954 if(this.lastActive == index){
24955 this.lastActive = false;
24958 var r = this.grid.store.getAt(index);
24963 this.selections.remove(r);
24964 //.console.log('deselectRow - record id :' + r.id);
24965 if(!preventViewNotify){
24967 var proxy = new Roo.Element(
24968 this.grid.getRowDom(index)
24970 proxy.removeClass('bg-info info');
24972 this.fireEvent("rowdeselect", this, index);
24973 this.fireEvent("selectionchange", this);
24977 restoreLast : function(){
24979 this.last = this._last;
24984 acceptsNav : function(row, col, cm){
24985 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24989 onEditorKey : function(field, e){
24990 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24995 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24997 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24999 }else if(k == e.ENTER && !e.ctrlKey){
25003 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25005 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25007 }else if(k == e.ESC){
25011 g.startEditing(newCell[0], newCell[1]);
25017 * Ext JS Library 1.1.1
25018 * Copyright(c) 2006-2007, Ext JS, LLC.
25020 * Originally Released Under LGPL - original licence link has changed is not relivant.
25023 * <script type="text/javascript">
25027 * @class Roo.bootstrap.PagingToolbar
25028 * @extends Roo.bootstrap.NavSimplebar
25029 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25031 * Create a new PagingToolbar
25032 * @param {Object} config The config object
25033 * @param {Roo.data.Store} store
25035 Roo.bootstrap.PagingToolbar = function(config)
25037 // old args format still supported... - xtype is prefered..
25038 // created from xtype...
25040 this.ds = config.dataSource;
25042 if (config.store && !this.ds) {
25043 this.store= Roo.factory(config.store, Roo.data);
25044 this.ds = this.store;
25045 this.ds.xmodule = this.xmodule || false;
25048 this.toolbarItems = [];
25049 if (config.items) {
25050 this.toolbarItems = config.items;
25053 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25058 this.bind(this.ds);
25061 if (Roo.bootstrap.version == 4) {
25062 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25064 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25069 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25071 * @cfg {Roo.data.Store} dataSource
25072 * The underlying data store providing the paged data
25075 * @cfg {String/HTMLElement/Element} container
25076 * container The id or element that will contain the toolbar
25079 * @cfg {Boolean} displayInfo
25080 * True to display the displayMsg (defaults to false)
25083 * @cfg {Number} pageSize
25084 * The number of records to display per page (defaults to 20)
25088 * @cfg {String} displayMsg
25089 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25091 displayMsg : 'Displaying {0} - {1} of {2}',
25093 * @cfg {String} emptyMsg
25094 * The message to display when no records are found (defaults to "No data to display")
25096 emptyMsg : 'No data to display',
25098 * Customizable piece of the default paging text (defaults to "Page")
25101 beforePageText : "Page",
25103 * Customizable piece of the default paging text (defaults to "of %0")
25106 afterPageText : "of {0}",
25108 * Customizable piece of the default paging text (defaults to "First Page")
25111 firstText : "First Page",
25113 * Customizable piece of the default paging text (defaults to "Previous Page")
25116 prevText : "Previous Page",
25118 * Customizable piece of the default paging text (defaults to "Next Page")
25121 nextText : "Next Page",
25123 * Customizable piece of the default paging text (defaults to "Last Page")
25126 lastText : "Last Page",
25128 * Customizable piece of the default paging text (defaults to "Refresh")
25131 refreshText : "Refresh",
25135 onRender : function(ct, position)
25137 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25138 this.navgroup.parentId = this.id;
25139 this.navgroup.onRender(this.el, null);
25140 // add the buttons to the navgroup
25142 if(this.displayInfo){
25143 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25144 this.displayEl = this.el.select('.x-paging-info', true).first();
25145 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25146 // this.displayEl = navel.el.select('span',true).first();
25152 Roo.each(_this.buttons, function(e){ // this might need to use render????
25153 Roo.factory(e).render(_this.el);
25157 Roo.each(_this.toolbarItems, function(e) {
25158 _this.navgroup.addItem(e);
25162 this.first = this.navgroup.addItem({
25163 tooltip: this.firstText,
25164 cls: "prev btn-outline-secondary",
25165 html : ' <i class="fa fa-step-backward"></i>',
25167 preventDefault: true,
25168 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25171 this.prev = this.navgroup.addItem({
25172 tooltip: this.prevText,
25173 cls: "prev btn-outline-secondary",
25174 html : ' <i class="fa fa-backward"></i>',
25176 preventDefault: true,
25177 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
25179 //this.addSeparator();
25182 var field = this.navgroup.addItem( {
25184 cls : 'x-paging-position btn-outline-secondary',
25186 html : this.beforePageText +
25187 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25188 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
25191 this.field = field.el.select('input', true).first();
25192 this.field.on("keydown", this.onPagingKeydown, this);
25193 this.field.on("focus", function(){this.dom.select();});
25196 this.afterTextEl = field.el.select('.x-paging-after',true).first();
25197 //this.field.setHeight(18);
25198 //this.addSeparator();
25199 this.next = this.navgroup.addItem({
25200 tooltip: this.nextText,
25201 cls: "next btn-outline-secondary",
25202 html : ' <i class="fa fa-forward"></i>',
25204 preventDefault: true,
25205 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
25207 this.last = this.navgroup.addItem({
25208 tooltip: this.lastText,
25209 html : ' <i class="fa fa-step-forward"></i>',
25210 cls: "next btn-outline-secondary",
25212 preventDefault: true,
25213 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
25215 //this.addSeparator();
25216 this.loading = this.navgroup.addItem({
25217 tooltip: this.refreshText,
25218 cls: "btn-outline-secondary",
25219 html : ' <i class="fa fa-refresh"></i>',
25220 preventDefault: true,
25221 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25227 updateInfo : function(){
25228 if(this.displayEl){
25229 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25230 var msg = count == 0 ?
25234 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
25236 this.displayEl.update(msg);
25241 onLoad : function(ds, r, o)
25243 this.cursor = o.params.start ? o.params.start : 0;
25245 var d = this.getPageData(),
25250 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25251 this.field.dom.value = ap;
25252 this.first.setDisabled(ap == 1);
25253 this.prev.setDisabled(ap == 1);
25254 this.next.setDisabled(ap == ps);
25255 this.last.setDisabled(ap == ps);
25256 this.loading.enable();
25261 getPageData : function(){
25262 var total = this.ds.getTotalCount();
25265 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25266 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25271 onLoadError : function(){
25272 this.loading.enable();
25276 onPagingKeydown : function(e){
25277 var k = e.getKey();
25278 var d = this.getPageData();
25280 var v = this.field.dom.value, pageNum;
25281 if(!v || isNaN(pageNum = parseInt(v, 10))){
25282 this.field.dom.value = d.activePage;
25285 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25286 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25289 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))
25291 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25292 this.field.dom.value = pageNum;
25293 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25296 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25298 var v = this.field.dom.value, pageNum;
25299 var increment = (e.shiftKey) ? 10 : 1;
25300 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25303 if(!v || isNaN(pageNum = parseInt(v, 10))) {
25304 this.field.dom.value = d.activePage;
25307 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25309 this.field.dom.value = parseInt(v, 10) + increment;
25310 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25311 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25318 beforeLoad : function(){
25320 this.loading.disable();
25325 onClick : function(which){
25334 ds.load({params:{start: 0, limit: this.pageSize}});
25337 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25340 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25343 var total = ds.getTotalCount();
25344 var extra = total % this.pageSize;
25345 var lastStart = extra ? (total - extra) : total-this.pageSize;
25346 ds.load({params:{start: lastStart, limit: this.pageSize}});
25349 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25355 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25356 * @param {Roo.data.Store} store The data store to unbind
25358 unbind : function(ds){
25359 ds.un("beforeload", this.beforeLoad, this);
25360 ds.un("load", this.onLoad, this);
25361 ds.un("loadexception", this.onLoadError, this);
25362 ds.un("remove", this.updateInfo, this);
25363 ds.un("add", this.updateInfo, this);
25364 this.ds = undefined;
25368 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25369 * @param {Roo.data.Store} store The data store to bind
25371 bind : function(ds){
25372 ds.on("beforeload", this.beforeLoad, this);
25373 ds.on("load", this.onLoad, this);
25374 ds.on("loadexception", this.onLoadError, this);
25375 ds.on("remove", this.updateInfo, this);
25376 ds.on("add", this.updateInfo, this);
25387 * @class Roo.bootstrap.MessageBar
25388 * @extends Roo.bootstrap.Component
25389 * Bootstrap MessageBar class
25390 * @cfg {String} html contents of the MessageBar
25391 * @cfg {String} weight (info | success | warning | danger) default info
25392 * @cfg {String} beforeClass insert the bar before the given class
25393 * @cfg {Boolean} closable (true | false) default false
25394 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25397 * Create a new Element
25398 * @param {Object} config The config object
25401 Roo.bootstrap.MessageBar = function(config){
25402 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25405 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25411 beforeClass: 'bootstrap-sticky-wrap',
25413 getAutoCreate : function(){
25417 cls: 'alert alert-dismissable alert-' + this.weight,
25422 html: this.html || ''
25428 cfg.cls += ' alert-messages-fixed';
25442 onRender : function(ct, position)
25444 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25447 var cfg = Roo.apply({}, this.getAutoCreate());
25451 cfg.cls += ' ' + this.cls;
25454 cfg.style = this.style;
25456 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25458 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25461 this.el.select('>button.close').on('click', this.hide, this);
25467 if (!this.rendered) {
25473 this.fireEvent('show', this);
25479 if (!this.rendered) {
25485 this.fireEvent('hide', this);
25488 update : function()
25490 // var e = this.el.dom.firstChild;
25492 // if(this.closable){
25493 // e = e.nextSibling;
25496 // e.data = this.html || '';
25498 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25514 * @class Roo.bootstrap.Graph
25515 * @extends Roo.bootstrap.Component
25516 * Bootstrap Graph class
25520 @cfg {String} graphtype bar | vbar | pie
25521 @cfg {number} g_x coodinator | centre x (pie)
25522 @cfg {number} g_y coodinator | centre y (pie)
25523 @cfg {number} g_r radius (pie)
25524 @cfg {number} g_height height of the chart (respected by all elements in the set)
25525 @cfg {number} g_width width of the chart (respected by all elements in the set)
25526 @cfg {Object} title The title of the chart
25529 -opts (object) options for the chart
25531 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25532 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25534 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.
25535 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25537 o stretch (boolean)
25539 -opts (object) options for the pie
25542 o startAngle (number)
25543 o endAngle (number)
25547 * Create a new Input
25548 * @param {Object} config The config object
25551 Roo.bootstrap.Graph = function(config){
25552 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25558 * The img click event for the img.
25559 * @param {Roo.EventObject} e
25565 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25576 //g_colors: this.colors,
25583 getAutoCreate : function(){
25594 onRender : function(ct,position){
25597 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25599 if (typeof(Raphael) == 'undefined') {
25600 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25604 this.raphael = Raphael(this.el.dom);
25606 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25607 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25608 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25609 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25611 r.text(160, 10, "Single Series Chart").attr(txtattr);
25612 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25613 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25614 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25616 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25617 r.barchart(330, 10, 300, 220, data1);
25618 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25619 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25622 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25623 // r.barchart(30, 30, 560, 250, xdata, {
25624 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25625 // axis : "0 0 1 1",
25626 // axisxlabels : xdata
25627 // //yvalues : cols,
25630 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25632 // this.load(null,xdata,{
25633 // axis : "0 0 1 1",
25634 // axisxlabels : xdata
25639 load : function(graphtype,xdata,opts)
25641 this.raphael.clear();
25643 graphtype = this.graphtype;
25648 var r = this.raphael,
25649 fin = function () {
25650 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25652 fout = function () {
25653 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25655 pfin = function() {
25656 this.sector.stop();
25657 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25660 this.label[0].stop();
25661 this.label[0].attr({ r: 7.5 });
25662 this.label[1].attr({ "font-weight": 800 });
25665 pfout = function() {
25666 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25669 this.label[0].animate({ r: 5 }, 500, "bounce");
25670 this.label[1].attr({ "font-weight": 400 });
25676 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25679 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25682 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25683 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25685 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25692 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25697 setTitle: function(o)
25702 initEvents: function() {
25705 this.el.on('click', this.onClick, this);
25709 onClick : function(e)
25711 Roo.log('img onclick');
25712 this.fireEvent('click', this, e);
25724 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25727 * @class Roo.bootstrap.dash.NumberBox
25728 * @extends Roo.bootstrap.Component
25729 * Bootstrap NumberBox class
25730 * @cfg {String} headline Box headline
25731 * @cfg {String} content Box content
25732 * @cfg {String} icon Box icon
25733 * @cfg {String} footer Footer text
25734 * @cfg {String} fhref Footer href
25737 * Create a new NumberBox
25738 * @param {Object} config The config object
25742 Roo.bootstrap.dash.NumberBox = function(config){
25743 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25747 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25756 getAutoCreate : function(){
25760 cls : 'small-box ',
25768 cls : 'roo-headline',
25769 html : this.headline
25773 cls : 'roo-content',
25774 html : this.content
25788 cls : 'ion ' + this.icon
25797 cls : 'small-box-footer',
25798 href : this.fhref || '#',
25802 cfg.cn.push(footer);
25809 onRender : function(ct,position){
25810 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25817 setHeadline: function (value)
25819 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25822 setFooter: function (value, href)
25824 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25827 this.el.select('a.small-box-footer',true).first().attr('href', href);
25832 setContent: function (value)
25834 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25837 initEvents: function()
25851 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25854 * @class Roo.bootstrap.dash.TabBox
25855 * @extends Roo.bootstrap.Component
25856 * Bootstrap TabBox class
25857 * @cfg {String} title Title of the TabBox
25858 * @cfg {String} icon Icon of the TabBox
25859 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25860 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25863 * Create a new TabBox
25864 * @param {Object} config The config object
25868 Roo.bootstrap.dash.TabBox = function(config){
25869 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25874 * When a pane is added
25875 * @param {Roo.bootstrap.dash.TabPane} pane
25879 * @event activatepane
25880 * When a pane is activated
25881 * @param {Roo.bootstrap.dash.TabPane} pane
25883 "activatepane" : true
25891 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25896 tabScrollable : false,
25898 getChildContainer : function()
25900 return this.el.select('.tab-content', true).first();
25903 getAutoCreate : function(){
25907 cls: 'pull-left header',
25915 cls: 'fa ' + this.icon
25921 cls: 'nav nav-tabs pull-right',
25927 if(this.tabScrollable){
25934 cls: 'nav nav-tabs pull-right',
25945 cls: 'nav-tabs-custom',
25950 cls: 'tab-content no-padding',
25958 initEvents : function()
25960 //Roo.log('add add pane handler');
25961 this.on('addpane', this.onAddPane, this);
25964 * Updates the box title
25965 * @param {String} html to set the title to.
25967 setTitle : function(value)
25969 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25971 onAddPane : function(pane)
25973 this.panes.push(pane);
25974 //Roo.log('addpane');
25976 // tabs are rendere left to right..
25977 if(!this.showtabs){
25981 var ctr = this.el.select('.nav-tabs', true).first();
25984 var existing = ctr.select('.nav-tab',true);
25985 var qty = existing.getCount();;
25988 var tab = ctr.createChild({
25990 cls : 'nav-tab' + (qty ? '' : ' active'),
25998 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26001 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26003 pane.el.addClass('active');
26008 onTabClick : function(ev,un,ob,pane)
26010 //Roo.log('tab - prev default');
26011 ev.preventDefault();
26014 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26015 pane.tab.addClass('active');
26016 //Roo.log(pane.title);
26017 this.getChildContainer().select('.tab-pane',true).removeClass('active');
26018 // technically we should have a deactivate event.. but maybe add later.
26019 // and it should not de-activate the selected tab...
26020 this.fireEvent('activatepane', pane);
26021 pane.el.addClass('active');
26022 pane.fireEvent('activate');
26027 getActivePane : function()
26030 Roo.each(this.panes, function(p) {
26031 if(p.el.hasClass('active')){
26052 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26054 * @class Roo.bootstrap.TabPane
26055 * @extends Roo.bootstrap.Component
26056 * Bootstrap TabPane class
26057 * @cfg {Boolean} active (false | true) Default false
26058 * @cfg {String} title title of panel
26062 * Create a new TabPane
26063 * @param {Object} config The config object
26066 Roo.bootstrap.dash.TabPane = function(config){
26067 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26073 * When a pane is activated
26074 * @param {Roo.bootstrap.dash.TabPane} pane
26081 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
26086 // the tabBox that this is attached to.
26089 getAutoCreate : function()
26097 cfg.cls += ' active';
26102 initEvents : function()
26104 //Roo.log('trigger add pane handler');
26105 this.parent().fireEvent('addpane', this)
26109 * Updates the tab title
26110 * @param {String} html to set the title to.
26112 setTitle: function(str)
26118 this.tab.select('a', true).first().dom.innerHTML = str;
26135 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26138 * @class Roo.bootstrap.menu.Menu
26139 * @extends Roo.bootstrap.Component
26140 * Bootstrap Menu class - container for Menu
26141 * @cfg {String} html Text of the menu
26142 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26143 * @cfg {String} icon Font awesome icon
26144 * @cfg {String} pos Menu align to (top | bottom) default bottom
26148 * Create a new Menu
26149 * @param {Object} config The config object
26153 Roo.bootstrap.menu.Menu = function(config){
26154 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26158 * @event beforeshow
26159 * Fires before this menu is displayed
26160 * @param {Roo.bootstrap.menu.Menu} this
26164 * @event beforehide
26165 * Fires before this menu is hidden
26166 * @param {Roo.bootstrap.menu.Menu} this
26171 * Fires after this menu is displayed
26172 * @param {Roo.bootstrap.menu.Menu} this
26177 * Fires after this menu is hidden
26178 * @param {Roo.bootstrap.menu.Menu} this
26183 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26184 * @param {Roo.bootstrap.menu.Menu} this
26185 * @param {Roo.EventObject} e
26192 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
26196 weight : 'default',
26201 getChildContainer : function() {
26202 if(this.isSubMenu){
26206 return this.el.select('ul.dropdown-menu', true).first();
26209 getAutoCreate : function()
26214 cls : 'roo-menu-text',
26222 cls : 'fa ' + this.icon
26233 cls : 'dropdown-button btn btn-' + this.weight,
26238 cls : 'dropdown-toggle btn btn-' + this.weight,
26248 cls : 'dropdown-menu'
26254 if(this.pos == 'top'){
26255 cfg.cls += ' dropup';
26258 if(this.isSubMenu){
26261 cls : 'dropdown-menu'
26268 onRender : function(ct, position)
26270 this.isSubMenu = ct.hasClass('dropdown-submenu');
26272 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26275 initEvents : function()
26277 if(this.isSubMenu){
26281 this.hidden = true;
26283 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26284 this.triggerEl.on('click', this.onTriggerPress, this);
26286 this.buttonEl = this.el.select('button.dropdown-button', true).first();
26287 this.buttonEl.on('click', this.onClick, this);
26293 if(this.isSubMenu){
26297 return this.el.select('ul.dropdown-menu', true).first();
26300 onClick : function(e)
26302 this.fireEvent("click", this, e);
26305 onTriggerPress : function(e)
26307 if (this.isVisible()) {
26314 isVisible : function(){
26315 return !this.hidden;
26320 this.fireEvent("beforeshow", this);
26322 this.hidden = false;
26323 this.el.addClass('open');
26325 Roo.get(document).on("mouseup", this.onMouseUp, this);
26327 this.fireEvent("show", this);
26334 this.fireEvent("beforehide", this);
26336 this.hidden = true;
26337 this.el.removeClass('open');
26339 Roo.get(document).un("mouseup", this.onMouseUp);
26341 this.fireEvent("hide", this);
26344 onMouseUp : function()
26358 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26361 * @class Roo.bootstrap.menu.Item
26362 * @extends Roo.bootstrap.Component
26363 * Bootstrap MenuItem class
26364 * @cfg {Boolean} submenu (true | false) default false
26365 * @cfg {String} html text of the item
26366 * @cfg {String} href the link
26367 * @cfg {Boolean} disable (true | false) default false
26368 * @cfg {Boolean} preventDefault (true | false) default true
26369 * @cfg {String} icon Font awesome icon
26370 * @cfg {String} pos Submenu align to (left | right) default right
26374 * Create a new Item
26375 * @param {Object} config The config object
26379 Roo.bootstrap.menu.Item = function(config){
26380 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26384 * Fires when the mouse is hovering over this menu
26385 * @param {Roo.bootstrap.menu.Item} this
26386 * @param {Roo.EventObject} e
26391 * Fires when the mouse exits this menu
26392 * @param {Roo.bootstrap.menu.Item} this
26393 * @param {Roo.EventObject} e
26399 * The raw click event for the entire grid.
26400 * @param {Roo.EventObject} e
26406 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26411 preventDefault: true,
26416 getAutoCreate : function()
26421 cls : 'roo-menu-item-text',
26429 cls : 'fa ' + this.icon
26438 href : this.href || '#',
26445 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26449 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26451 if(this.pos == 'left'){
26452 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26459 initEvents : function()
26461 this.el.on('mouseover', this.onMouseOver, this);
26462 this.el.on('mouseout', this.onMouseOut, this);
26464 this.el.select('a', true).first().on('click', this.onClick, this);
26468 onClick : function(e)
26470 if(this.preventDefault){
26471 e.preventDefault();
26474 this.fireEvent("click", this, e);
26477 onMouseOver : function(e)
26479 if(this.submenu && this.pos == 'left'){
26480 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26483 this.fireEvent("mouseover", this, e);
26486 onMouseOut : function(e)
26488 this.fireEvent("mouseout", this, e);
26500 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26503 * @class Roo.bootstrap.menu.Separator
26504 * @extends Roo.bootstrap.Component
26505 * Bootstrap Separator class
26508 * Create a new Separator
26509 * @param {Object} config The config object
26513 Roo.bootstrap.menu.Separator = function(config){
26514 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26517 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26519 getAutoCreate : function(){
26540 * @class Roo.bootstrap.Tooltip
26541 * Bootstrap Tooltip class
26542 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26543 * to determine which dom element triggers the tooltip.
26545 * It needs to add support for additional attributes like tooltip-position
26548 * Create a new Toolti
26549 * @param {Object} config The config object
26552 Roo.bootstrap.Tooltip = function(config){
26553 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26555 this.alignment = Roo.bootstrap.Tooltip.alignment;
26557 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26558 this.alignment = config.alignment;
26563 Roo.apply(Roo.bootstrap.Tooltip, {
26565 * @function init initialize tooltip monitoring.
26569 currentTip : false,
26570 currentRegion : false,
26576 Roo.get(document).on('mouseover', this.enter ,this);
26577 Roo.get(document).on('mouseout', this.leave, this);
26580 this.currentTip = new Roo.bootstrap.Tooltip();
26583 enter : function(ev)
26585 var dom = ev.getTarget();
26587 //Roo.log(['enter',dom]);
26588 var el = Roo.fly(dom);
26589 if (this.currentEl) {
26591 //Roo.log(this.currentEl);
26592 //Roo.log(this.currentEl.contains(dom));
26593 if (this.currentEl == el) {
26596 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26602 if (this.currentTip.el) {
26603 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26607 if(!el || el.dom == document){
26613 // you can not look for children, as if el is the body.. then everythign is the child..
26614 if (!el.attr('tooltip')) { //
26615 if (!el.select("[tooltip]").elements.length) {
26618 // is the mouse over this child...?
26619 bindEl = el.select("[tooltip]").first();
26620 var xy = ev.getXY();
26621 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26622 //Roo.log("not in region.");
26625 //Roo.log("child element over..");
26628 this.currentEl = bindEl;
26629 this.currentTip.bind(bindEl);
26630 this.currentRegion = Roo.lib.Region.getRegion(dom);
26631 this.currentTip.enter();
26634 leave : function(ev)
26636 var dom = ev.getTarget();
26637 //Roo.log(['leave',dom]);
26638 if (!this.currentEl) {
26643 if (dom != this.currentEl.dom) {
26646 var xy = ev.getXY();
26647 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26650 // only activate leave if mouse cursor is outside... bounding box..
26655 if (this.currentTip) {
26656 this.currentTip.leave();
26658 //Roo.log('clear currentEl');
26659 this.currentEl = false;
26664 'left' : ['r-l', [-2,0], 'right'],
26665 'right' : ['l-r', [2,0], 'left'],
26666 'bottom' : ['t-b', [0,2], 'top'],
26667 'top' : [ 'b-t', [0,-2], 'bottom']
26673 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26678 delay : null, // can be { show : 300 , hide: 500}
26682 hoverState : null, //???
26684 placement : 'bottom',
26688 getAutoCreate : function(){
26695 cls : 'tooltip-arrow'
26698 cls : 'tooltip-inner'
26705 bind : function(el)
26711 enter : function () {
26713 if (this.timeout != null) {
26714 clearTimeout(this.timeout);
26717 this.hoverState = 'in';
26718 //Roo.log("enter - show");
26719 if (!this.delay || !this.delay.show) {
26724 this.timeout = setTimeout(function () {
26725 if (_t.hoverState == 'in') {
26728 }, this.delay.show);
26732 clearTimeout(this.timeout);
26734 this.hoverState = 'out';
26735 if (!this.delay || !this.delay.hide) {
26741 this.timeout = setTimeout(function () {
26742 //Roo.log("leave - timeout");
26744 if (_t.hoverState == 'out') {
26746 Roo.bootstrap.Tooltip.currentEl = false;
26751 show : function (msg)
26754 this.render(document.body);
26757 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26759 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26761 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26763 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26765 var placement = typeof this.placement == 'function' ?
26766 this.placement.call(this, this.el, on_el) :
26769 var autoToken = /\s?auto?\s?/i;
26770 var autoPlace = autoToken.test(placement);
26772 placement = placement.replace(autoToken, '') || 'top';
26776 //this.el.setXY([0,0]);
26778 //this.el.dom.style.display='block';
26780 //this.el.appendTo(on_el);
26782 var p = this.getPosition();
26783 var box = this.el.getBox();
26789 var align = this.alignment[placement];
26791 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26793 if(placement == 'top' || placement == 'bottom'){
26795 placement = 'right';
26798 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26799 placement = 'left';
26802 var scroll = Roo.select('body', true).first().getScroll();
26804 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26808 align = this.alignment[placement];
26811 this.el.alignTo(this.bindEl, align[0],align[1]);
26812 //var arrow = this.el.select('.arrow',true).first();
26813 //arrow.set(align[2],
26815 this.el.addClass(placement);
26817 this.el.addClass('in fade');
26819 this.hoverState = null;
26821 if (this.el.hasClass('fade')) {
26832 //this.el.setXY([0,0]);
26833 this.el.removeClass('in');
26849 * @class Roo.bootstrap.LocationPicker
26850 * @extends Roo.bootstrap.Component
26851 * Bootstrap LocationPicker class
26852 * @cfg {Number} latitude Position when init default 0
26853 * @cfg {Number} longitude Position when init default 0
26854 * @cfg {Number} zoom default 15
26855 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26856 * @cfg {Boolean} mapTypeControl default false
26857 * @cfg {Boolean} disableDoubleClickZoom default false
26858 * @cfg {Boolean} scrollwheel default true
26859 * @cfg {Boolean} streetViewControl default false
26860 * @cfg {Number} radius default 0
26861 * @cfg {String} locationName
26862 * @cfg {Boolean} draggable default true
26863 * @cfg {Boolean} enableAutocomplete default false
26864 * @cfg {Boolean} enableReverseGeocode default true
26865 * @cfg {String} markerTitle
26868 * Create a new LocationPicker
26869 * @param {Object} config The config object
26873 Roo.bootstrap.LocationPicker = function(config){
26875 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26880 * Fires when the picker initialized.
26881 * @param {Roo.bootstrap.LocationPicker} this
26882 * @param {Google Location} location
26886 * @event positionchanged
26887 * Fires when the picker position changed.
26888 * @param {Roo.bootstrap.LocationPicker} this
26889 * @param {Google Location} location
26891 positionchanged : true,
26894 * Fires when the map resize.
26895 * @param {Roo.bootstrap.LocationPicker} this
26900 * Fires when the map show.
26901 * @param {Roo.bootstrap.LocationPicker} this
26906 * Fires when the map hide.
26907 * @param {Roo.bootstrap.LocationPicker} this
26912 * Fires when click the map.
26913 * @param {Roo.bootstrap.LocationPicker} this
26914 * @param {Map event} e
26918 * @event mapRightClick
26919 * Fires when right click the map.
26920 * @param {Roo.bootstrap.LocationPicker} this
26921 * @param {Map event} e
26923 mapRightClick : true,
26925 * @event markerClick
26926 * Fires when click the marker.
26927 * @param {Roo.bootstrap.LocationPicker} this
26928 * @param {Map event} e
26930 markerClick : true,
26932 * @event markerRightClick
26933 * Fires when right click the marker.
26934 * @param {Roo.bootstrap.LocationPicker} this
26935 * @param {Map event} e
26937 markerRightClick : true,
26939 * @event OverlayViewDraw
26940 * Fires when OverlayView Draw
26941 * @param {Roo.bootstrap.LocationPicker} this
26943 OverlayViewDraw : true,
26945 * @event OverlayViewOnAdd
26946 * Fires when OverlayView Draw
26947 * @param {Roo.bootstrap.LocationPicker} this
26949 OverlayViewOnAdd : true,
26951 * @event OverlayViewOnRemove
26952 * Fires when OverlayView Draw
26953 * @param {Roo.bootstrap.LocationPicker} this
26955 OverlayViewOnRemove : true,
26957 * @event OverlayViewShow
26958 * Fires when OverlayView Draw
26959 * @param {Roo.bootstrap.LocationPicker} this
26960 * @param {Pixel} cpx
26962 OverlayViewShow : true,
26964 * @event OverlayViewHide
26965 * Fires when OverlayView Draw
26966 * @param {Roo.bootstrap.LocationPicker} this
26968 OverlayViewHide : true,
26970 * @event loadexception
26971 * Fires when load google lib failed.
26972 * @param {Roo.bootstrap.LocationPicker} this
26974 loadexception : true
26979 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26981 gMapContext: false,
26987 mapTypeControl: false,
26988 disableDoubleClickZoom: false,
26990 streetViewControl: false,
26994 enableAutocomplete: false,
26995 enableReverseGeocode: true,
26998 getAutoCreate: function()
27003 cls: 'roo-location-picker'
27009 initEvents: function(ct, position)
27011 if(!this.el.getWidth() || this.isApplied()){
27015 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27020 initial: function()
27022 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27023 this.fireEvent('loadexception', this);
27027 if(!this.mapTypeId){
27028 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27031 this.gMapContext = this.GMapContext();
27033 this.initOverlayView();
27035 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27039 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27040 _this.setPosition(_this.gMapContext.marker.position);
27043 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27044 _this.fireEvent('mapClick', this, event);
27048 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27049 _this.fireEvent('mapRightClick', this, event);
27053 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27054 _this.fireEvent('markerClick', this, event);
27058 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27059 _this.fireEvent('markerRightClick', this, event);
27063 this.setPosition(this.gMapContext.location);
27065 this.fireEvent('initial', this, this.gMapContext.location);
27068 initOverlayView: function()
27072 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27076 _this.fireEvent('OverlayViewDraw', _this);
27081 _this.fireEvent('OverlayViewOnAdd', _this);
27084 onRemove: function()
27086 _this.fireEvent('OverlayViewOnRemove', _this);
27089 show: function(cpx)
27091 _this.fireEvent('OverlayViewShow', _this, cpx);
27096 _this.fireEvent('OverlayViewHide', _this);
27102 fromLatLngToContainerPixel: function(event)
27104 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27107 isApplied: function()
27109 return this.getGmapContext() == false ? false : true;
27112 getGmapContext: function()
27114 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27117 GMapContext: function()
27119 var position = new google.maps.LatLng(this.latitude, this.longitude);
27121 var _map = new google.maps.Map(this.el.dom, {
27124 mapTypeId: this.mapTypeId,
27125 mapTypeControl: this.mapTypeControl,
27126 disableDoubleClickZoom: this.disableDoubleClickZoom,
27127 scrollwheel: this.scrollwheel,
27128 streetViewControl: this.streetViewControl,
27129 locationName: this.locationName,
27130 draggable: this.draggable,
27131 enableAutocomplete: this.enableAutocomplete,
27132 enableReverseGeocode: this.enableReverseGeocode
27135 var _marker = new google.maps.Marker({
27136 position: position,
27138 title: this.markerTitle,
27139 draggable: this.draggable
27146 location: position,
27147 radius: this.radius,
27148 locationName: this.locationName,
27149 addressComponents: {
27150 formatted_address: null,
27151 addressLine1: null,
27152 addressLine2: null,
27154 streetNumber: null,
27158 stateOrProvince: null
27161 domContainer: this.el.dom,
27162 geodecoder: new google.maps.Geocoder()
27166 drawCircle: function(center, radius, options)
27168 if (this.gMapContext.circle != null) {
27169 this.gMapContext.circle.setMap(null);
27173 options = Roo.apply({}, options, {
27174 strokeColor: "#0000FF",
27175 strokeOpacity: .35,
27177 fillColor: "#0000FF",
27181 options.map = this.gMapContext.map;
27182 options.radius = radius;
27183 options.center = center;
27184 this.gMapContext.circle = new google.maps.Circle(options);
27185 return this.gMapContext.circle;
27191 setPosition: function(location)
27193 this.gMapContext.location = location;
27194 this.gMapContext.marker.setPosition(location);
27195 this.gMapContext.map.panTo(location);
27196 this.drawCircle(location, this.gMapContext.radius, {});
27200 if (this.gMapContext.settings.enableReverseGeocode) {
27201 this.gMapContext.geodecoder.geocode({
27202 latLng: this.gMapContext.location
27203 }, function(results, status) {
27205 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27206 _this.gMapContext.locationName = results[0].formatted_address;
27207 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27209 _this.fireEvent('positionchanged', this, location);
27216 this.fireEvent('positionchanged', this, location);
27221 google.maps.event.trigger(this.gMapContext.map, "resize");
27223 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27225 this.fireEvent('resize', this);
27228 setPositionByLatLng: function(latitude, longitude)
27230 this.setPosition(new google.maps.LatLng(latitude, longitude));
27233 getCurrentPosition: function()
27236 latitude: this.gMapContext.location.lat(),
27237 longitude: this.gMapContext.location.lng()
27241 getAddressName: function()
27243 return this.gMapContext.locationName;
27246 getAddressComponents: function()
27248 return this.gMapContext.addressComponents;
27251 address_component_from_google_geocode: function(address_components)
27255 for (var i = 0; i < address_components.length; i++) {
27256 var component = address_components[i];
27257 if (component.types.indexOf("postal_code") >= 0) {
27258 result.postalCode = component.short_name;
27259 } else if (component.types.indexOf("street_number") >= 0) {
27260 result.streetNumber = component.short_name;
27261 } else if (component.types.indexOf("route") >= 0) {
27262 result.streetName = component.short_name;
27263 } else if (component.types.indexOf("neighborhood") >= 0) {
27264 result.city = component.short_name;
27265 } else if (component.types.indexOf("locality") >= 0) {
27266 result.city = component.short_name;
27267 } else if (component.types.indexOf("sublocality") >= 0) {
27268 result.district = component.short_name;
27269 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27270 result.stateOrProvince = component.short_name;
27271 } else if (component.types.indexOf("country") >= 0) {
27272 result.country = component.short_name;
27276 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27277 result.addressLine2 = "";
27281 setZoomLevel: function(zoom)
27283 this.gMapContext.map.setZoom(zoom);
27296 this.fireEvent('show', this);
27307 this.fireEvent('hide', this);
27312 Roo.apply(Roo.bootstrap.LocationPicker, {
27314 OverlayView : function(map, options)
27316 options = options || {};
27323 * @class Roo.bootstrap.Alert
27324 * @extends Roo.bootstrap.Component
27325 * Bootstrap Alert class - shows an alert area box
27327 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27328 Enter a valid email address
27331 * @cfg {String} title The title of alert
27332 * @cfg {String} html The content of alert
27333 * @cfg {String} weight ( success | info | warning | danger )
27334 * @cfg {String} faicon font-awesomeicon
27337 * Create a new alert
27338 * @param {Object} config The config object
27342 Roo.bootstrap.Alert = function(config){
27343 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27347 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27354 getAutoCreate : function()
27363 cls : 'roo-alert-icon'
27368 cls : 'roo-alert-title',
27373 cls : 'roo-alert-text',
27380 cfg.cn[0].cls += ' fa ' + this.faicon;
27384 cfg.cls += ' alert-' + this.weight;
27390 initEvents: function()
27392 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27395 setTitle : function(str)
27397 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27400 setText : function(str)
27402 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27405 setWeight : function(weight)
27408 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27411 this.weight = weight;
27413 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27416 setIcon : function(icon)
27419 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27422 this.faicon = icon;
27424 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27445 * @class Roo.bootstrap.UploadCropbox
27446 * @extends Roo.bootstrap.Component
27447 * Bootstrap UploadCropbox class
27448 * @cfg {String} emptyText show when image has been loaded
27449 * @cfg {String} rotateNotify show when image too small to rotate
27450 * @cfg {Number} errorTimeout default 3000
27451 * @cfg {Number} minWidth default 300
27452 * @cfg {Number} minHeight default 300
27453 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27454 * @cfg {Boolean} isDocument (true|false) default false
27455 * @cfg {String} url action url
27456 * @cfg {String} paramName default 'imageUpload'
27457 * @cfg {String} method default POST
27458 * @cfg {Boolean} loadMask (true|false) default true
27459 * @cfg {Boolean} loadingText default 'Loading...'
27462 * Create a new UploadCropbox
27463 * @param {Object} config The config object
27466 Roo.bootstrap.UploadCropbox = function(config){
27467 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27471 * @event beforeselectfile
27472 * Fire before select file
27473 * @param {Roo.bootstrap.UploadCropbox} this
27475 "beforeselectfile" : true,
27478 * Fire after initEvent
27479 * @param {Roo.bootstrap.UploadCropbox} this
27484 * Fire after initEvent
27485 * @param {Roo.bootstrap.UploadCropbox} this
27486 * @param {String} data
27491 * Fire when preparing the file data
27492 * @param {Roo.bootstrap.UploadCropbox} this
27493 * @param {Object} file
27498 * Fire when get exception
27499 * @param {Roo.bootstrap.UploadCropbox} this
27500 * @param {XMLHttpRequest} xhr
27502 "exception" : true,
27504 * @event beforeloadcanvas
27505 * Fire before load the canvas
27506 * @param {Roo.bootstrap.UploadCropbox} this
27507 * @param {String} src
27509 "beforeloadcanvas" : true,
27512 * Fire when trash image
27513 * @param {Roo.bootstrap.UploadCropbox} this
27518 * Fire when download the image
27519 * @param {Roo.bootstrap.UploadCropbox} this
27523 * @event footerbuttonclick
27524 * Fire when footerbuttonclick
27525 * @param {Roo.bootstrap.UploadCropbox} this
27526 * @param {String} type
27528 "footerbuttonclick" : true,
27532 * @param {Roo.bootstrap.UploadCropbox} this
27537 * Fire when rotate the image
27538 * @param {Roo.bootstrap.UploadCropbox} this
27539 * @param {String} pos
27544 * Fire when inspect the file
27545 * @param {Roo.bootstrap.UploadCropbox} this
27546 * @param {Object} file
27551 * Fire when xhr upload the file
27552 * @param {Roo.bootstrap.UploadCropbox} this
27553 * @param {Object} data
27558 * Fire when arrange the file data
27559 * @param {Roo.bootstrap.UploadCropbox} this
27560 * @param {Object} formData
27565 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27568 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27570 emptyText : 'Click to upload image',
27571 rotateNotify : 'Image is too small to rotate',
27572 errorTimeout : 3000,
27586 cropType : 'image/jpeg',
27588 canvasLoaded : false,
27589 isDocument : false,
27591 paramName : 'imageUpload',
27593 loadingText : 'Loading...',
27596 getAutoCreate : function()
27600 cls : 'roo-upload-cropbox',
27604 cls : 'roo-upload-cropbox-selector',
27609 cls : 'roo-upload-cropbox-body',
27610 style : 'cursor:pointer',
27614 cls : 'roo-upload-cropbox-preview'
27618 cls : 'roo-upload-cropbox-thumb'
27622 cls : 'roo-upload-cropbox-empty-notify',
27623 html : this.emptyText
27627 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27628 html : this.rotateNotify
27634 cls : 'roo-upload-cropbox-footer',
27637 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27647 onRender : function(ct, position)
27649 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27651 if (this.buttons.length) {
27653 Roo.each(this.buttons, function(bb) {
27655 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27657 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27663 this.maskEl = this.el;
27667 initEvents : function()
27669 this.urlAPI = (window.createObjectURL && window) ||
27670 (window.URL && URL.revokeObjectURL && URL) ||
27671 (window.webkitURL && webkitURL);
27673 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27674 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27676 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27677 this.selectorEl.hide();
27679 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27680 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27682 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27683 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27684 this.thumbEl.hide();
27686 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27687 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27689 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27690 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27691 this.errorEl.hide();
27693 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27694 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27695 this.footerEl.hide();
27697 this.setThumbBoxSize();
27703 this.fireEvent('initial', this);
27710 window.addEventListener("resize", function() { _this.resize(); } );
27712 this.bodyEl.on('click', this.beforeSelectFile, this);
27715 this.bodyEl.on('touchstart', this.onTouchStart, this);
27716 this.bodyEl.on('touchmove', this.onTouchMove, this);
27717 this.bodyEl.on('touchend', this.onTouchEnd, this);
27721 this.bodyEl.on('mousedown', this.onMouseDown, this);
27722 this.bodyEl.on('mousemove', this.onMouseMove, this);
27723 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27724 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27725 Roo.get(document).on('mouseup', this.onMouseUp, this);
27728 this.selectorEl.on('change', this.onFileSelected, this);
27734 this.baseScale = 1;
27736 this.baseRotate = 1;
27737 this.dragable = false;
27738 this.pinching = false;
27741 this.cropData = false;
27742 this.notifyEl.dom.innerHTML = this.emptyText;
27744 this.selectorEl.dom.value = '';
27748 resize : function()
27750 if(this.fireEvent('resize', this) != false){
27751 this.setThumbBoxPosition();
27752 this.setCanvasPosition();
27756 onFooterButtonClick : function(e, el, o, type)
27759 case 'rotate-left' :
27760 this.onRotateLeft(e);
27762 case 'rotate-right' :
27763 this.onRotateRight(e);
27766 this.beforeSelectFile(e);
27781 this.fireEvent('footerbuttonclick', this, type);
27784 beforeSelectFile : function(e)
27786 e.preventDefault();
27788 if(this.fireEvent('beforeselectfile', this) != false){
27789 this.selectorEl.dom.click();
27793 onFileSelected : function(e)
27795 e.preventDefault();
27797 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27801 var file = this.selectorEl.dom.files[0];
27803 if(this.fireEvent('inspect', this, file) != false){
27804 this.prepare(file);
27809 trash : function(e)
27811 this.fireEvent('trash', this);
27814 download : function(e)
27816 this.fireEvent('download', this);
27819 loadCanvas : function(src)
27821 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27825 this.imageEl = document.createElement('img');
27829 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27831 this.imageEl.src = src;
27835 onLoadCanvas : function()
27837 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27838 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27840 this.bodyEl.un('click', this.beforeSelectFile, this);
27842 this.notifyEl.hide();
27843 this.thumbEl.show();
27844 this.footerEl.show();
27846 this.baseRotateLevel();
27848 if(this.isDocument){
27849 this.setThumbBoxSize();
27852 this.setThumbBoxPosition();
27854 this.baseScaleLevel();
27860 this.canvasLoaded = true;
27863 this.maskEl.unmask();
27868 setCanvasPosition : function()
27870 if(!this.canvasEl){
27874 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27875 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27877 this.previewEl.setLeft(pw);
27878 this.previewEl.setTop(ph);
27882 onMouseDown : function(e)
27886 this.dragable = true;
27887 this.pinching = false;
27889 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27890 this.dragable = false;
27894 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27895 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27899 onMouseMove : function(e)
27903 if(!this.canvasLoaded){
27907 if (!this.dragable){
27911 var minX = Math.ceil(this.thumbEl.getLeft(true));
27912 var minY = Math.ceil(this.thumbEl.getTop(true));
27914 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27915 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27917 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27918 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27920 x = x - this.mouseX;
27921 y = y - this.mouseY;
27923 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27924 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27926 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27927 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27929 this.previewEl.setLeft(bgX);
27930 this.previewEl.setTop(bgY);
27932 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27933 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27936 onMouseUp : function(e)
27940 this.dragable = false;
27943 onMouseWheel : function(e)
27947 this.startScale = this.scale;
27949 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27951 if(!this.zoomable()){
27952 this.scale = this.startScale;
27961 zoomable : function()
27963 var minScale = this.thumbEl.getWidth() / this.minWidth;
27965 if(this.minWidth < this.minHeight){
27966 minScale = this.thumbEl.getHeight() / this.minHeight;
27969 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27970 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27974 (this.rotate == 0 || this.rotate == 180) &&
27976 width > this.imageEl.OriginWidth ||
27977 height > this.imageEl.OriginHeight ||
27978 (width < this.minWidth && height < this.minHeight)
27986 (this.rotate == 90 || this.rotate == 270) &&
27988 width > this.imageEl.OriginWidth ||
27989 height > this.imageEl.OriginHeight ||
27990 (width < this.minHeight && height < this.minWidth)
27997 !this.isDocument &&
27998 (this.rotate == 0 || this.rotate == 180) &&
28000 width < this.minWidth ||
28001 width > this.imageEl.OriginWidth ||
28002 height < this.minHeight ||
28003 height > this.imageEl.OriginHeight
28010 !this.isDocument &&
28011 (this.rotate == 90 || this.rotate == 270) &&
28013 width < this.minHeight ||
28014 width > this.imageEl.OriginWidth ||
28015 height < this.minWidth ||
28016 height > this.imageEl.OriginHeight
28026 onRotateLeft : function(e)
28028 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28030 var minScale = this.thumbEl.getWidth() / this.minWidth;
28032 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28033 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28035 this.startScale = this.scale;
28037 while (this.getScaleLevel() < minScale){
28039 this.scale = this.scale + 1;
28041 if(!this.zoomable()){
28046 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28047 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28052 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28059 this.scale = this.startScale;
28061 this.onRotateFail();
28066 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28068 if(this.isDocument){
28069 this.setThumbBoxSize();
28070 this.setThumbBoxPosition();
28071 this.setCanvasPosition();
28076 this.fireEvent('rotate', this, 'left');
28080 onRotateRight : function(e)
28082 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28084 var minScale = this.thumbEl.getWidth() / this.minWidth;
28086 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28087 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28089 this.startScale = this.scale;
28091 while (this.getScaleLevel() < minScale){
28093 this.scale = this.scale + 1;
28095 if(!this.zoomable()){
28100 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28101 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28106 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28113 this.scale = this.startScale;
28115 this.onRotateFail();
28120 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28122 if(this.isDocument){
28123 this.setThumbBoxSize();
28124 this.setThumbBoxPosition();
28125 this.setCanvasPosition();
28130 this.fireEvent('rotate', this, 'right');
28133 onRotateFail : function()
28135 this.errorEl.show(true);
28139 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28144 this.previewEl.dom.innerHTML = '';
28146 var canvasEl = document.createElement("canvas");
28148 var contextEl = canvasEl.getContext("2d");
28150 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28151 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28152 var center = this.imageEl.OriginWidth / 2;
28154 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28155 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28156 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28157 center = this.imageEl.OriginHeight / 2;
28160 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28162 contextEl.translate(center, center);
28163 contextEl.rotate(this.rotate * Math.PI / 180);
28165 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28167 this.canvasEl = document.createElement("canvas");
28169 this.contextEl = this.canvasEl.getContext("2d");
28171 switch (this.rotate) {
28174 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28175 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28177 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28182 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28183 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28185 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28186 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);
28190 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28195 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28196 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28198 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28199 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);
28203 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);
28208 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28209 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28211 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28212 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28216 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);
28223 this.previewEl.appendChild(this.canvasEl);
28225 this.setCanvasPosition();
28230 if(!this.canvasLoaded){
28234 var imageCanvas = document.createElement("canvas");
28236 var imageContext = imageCanvas.getContext("2d");
28238 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28239 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28241 var center = imageCanvas.width / 2;
28243 imageContext.translate(center, center);
28245 imageContext.rotate(this.rotate * Math.PI / 180);
28247 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28249 var canvas = document.createElement("canvas");
28251 var context = canvas.getContext("2d");
28253 canvas.width = this.minWidth;
28254 canvas.height = this.minHeight;
28256 switch (this.rotate) {
28259 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28260 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28262 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28263 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28265 var targetWidth = this.minWidth - 2 * x;
28266 var targetHeight = this.minHeight - 2 * y;
28270 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28271 scale = targetWidth / width;
28274 if(x > 0 && y == 0){
28275 scale = targetHeight / height;
28278 if(x > 0 && y > 0){
28279 scale = targetWidth / width;
28281 if(width < height){
28282 scale = targetHeight / height;
28286 context.scale(scale, scale);
28288 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28289 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28291 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28292 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28294 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28299 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28300 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28302 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28303 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28305 var targetWidth = this.minWidth - 2 * x;
28306 var targetHeight = this.minHeight - 2 * y;
28310 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28311 scale = targetWidth / width;
28314 if(x > 0 && y == 0){
28315 scale = targetHeight / height;
28318 if(x > 0 && y > 0){
28319 scale = targetWidth / width;
28321 if(width < height){
28322 scale = targetHeight / height;
28326 context.scale(scale, scale);
28328 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28329 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28331 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28332 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28334 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28336 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28341 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28342 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28344 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28345 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28347 var targetWidth = this.minWidth - 2 * x;
28348 var targetHeight = this.minHeight - 2 * y;
28352 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28353 scale = targetWidth / width;
28356 if(x > 0 && y == 0){
28357 scale = targetHeight / height;
28360 if(x > 0 && y > 0){
28361 scale = targetWidth / width;
28363 if(width < height){
28364 scale = targetHeight / height;
28368 context.scale(scale, scale);
28370 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28371 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28373 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28374 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28376 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28377 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28379 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28384 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28385 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28387 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28388 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28390 var targetWidth = this.minWidth - 2 * x;
28391 var targetHeight = this.minHeight - 2 * y;
28395 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28396 scale = targetWidth / width;
28399 if(x > 0 && y == 0){
28400 scale = targetHeight / height;
28403 if(x > 0 && y > 0){
28404 scale = targetWidth / width;
28406 if(width < height){
28407 scale = targetHeight / height;
28411 context.scale(scale, scale);
28413 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28414 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28416 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28417 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28419 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28421 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28428 this.cropData = canvas.toDataURL(this.cropType);
28430 if(this.fireEvent('crop', this, this.cropData) !== false){
28431 this.process(this.file, this.cropData);
28438 setThumbBoxSize : function()
28442 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28443 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28444 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28446 this.minWidth = width;
28447 this.minHeight = height;
28449 if(this.rotate == 90 || this.rotate == 270){
28450 this.minWidth = height;
28451 this.minHeight = width;
28456 width = Math.ceil(this.minWidth * height / this.minHeight);
28458 if(this.minWidth > this.minHeight){
28460 height = Math.ceil(this.minHeight * width / this.minWidth);
28463 this.thumbEl.setStyle({
28464 width : width + 'px',
28465 height : height + 'px'
28472 setThumbBoxPosition : function()
28474 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28475 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28477 this.thumbEl.setLeft(x);
28478 this.thumbEl.setTop(y);
28482 baseRotateLevel : function()
28484 this.baseRotate = 1;
28487 typeof(this.exif) != 'undefined' &&
28488 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28489 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28491 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28494 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28498 baseScaleLevel : function()
28502 if(this.isDocument){
28504 if(this.baseRotate == 6 || this.baseRotate == 8){
28506 height = this.thumbEl.getHeight();
28507 this.baseScale = height / this.imageEl.OriginWidth;
28509 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28510 width = this.thumbEl.getWidth();
28511 this.baseScale = width / this.imageEl.OriginHeight;
28517 height = this.thumbEl.getHeight();
28518 this.baseScale = height / this.imageEl.OriginHeight;
28520 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28521 width = this.thumbEl.getWidth();
28522 this.baseScale = width / this.imageEl.OriginWidth;
28528 if(this.baseRotate == 6 || this.baseRotate == 8){
28530 width = this.thumbEl.getHeight();
28531 this.baseScale = width / this.imageEl.OriginHeight;
28533 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28534 height = this.thumbEl.getWidth();
28535 this.baseScale = height / this.imageEl.OriginHeight;
28538 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28539 height = this.thumbEl.getWidth();
28540 this.baseScale = height / this.imageEl.OriginHeight;
28542 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28543 width = this.thumbEl.getHeight();
28544 this.baseScale = width / this.imageEl.OriginWidth;
28551 width = this.thumbEl.getWidth();
28552 this.baseScale = width / this.imageEl.OriginWidth;
28554 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28555 height = this.thumbEl.getHeight();
28556 this.baseScale = height / this.imageEl.OriginHeight;
28559 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28561 height = this.thumbEl.getHeight();
28562 this.baseScale = height / this.imageEl.OriginHeight;
28564 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28565 width = this.thumbEl.getWidth();
28566 this.baseScale = width / this.imageEl.OriginWidth;
28574 getScaleLevel : function()
28576 return this.baseScale * Math.pow(1.1, this.scale);
28579 onTouchStart : function(e)
28581 if(!this.canvasLoaded){
28582 this.beforeSelectFile(e);
28586 var touches = e.browserEvent.touches;
28592 if(touches.length == 1){
28593 this.onMouseDown(e);
28597 if(touches.length != 2){
28603 for(var i = 0, finger; finger = touches[i]; i++){
28604 coords.push(finger.pageX, finger.pageY);
28607 var x = Math.pow(coords[0] - coords[2], 2);
28608 var y = Math.pow(coords[1] - coords[3], 2);
28610 this.startDistance = Math.sqrt(x + y);
28612 this.startScale = this.scale;
28614 this.pinching = true;
28615 this.dragable = false;
28619 onTouchMove : function(e)
28621 if(!this.pinching && !this.dragable){
28625 var touches = e.browserEvent.touches;
28632 this.onMouseMove(e);
28638 for(var i = 0, finger; finger = touches[i]; i++){
28639 coords.push(finger.pageX, finger.pageY);
28642 var x = Math.pow(coords[0] - coords[2], 2);
28643 var y = Math.pow(coords[1] - coords[3], 2);
28645 this.endDistance = Math.sqrt(x + y);
28647 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28649 if(!this.zoomable()){
28650 this.scale = this.startScale;
28658 onTouchEnd : function(e)
28660 this.pinching = false;
28661 this.dragable = false;
28665 process : function(file, crop)
28668 this.maskEl.mask(this.loadingText);
28671 this.xhr = new XMLHttpRequest();
28673 file.xhr = this.xhr;
28675 this.xhr.open(this.method, this.url, true);
28678 "Accept": "application/json",
28679 "Cache-Control": "no-cache",
28680 "X-Requested-With": "XMLHttpRequest"
28683 for (var headerName in headers) {
28684 var headerValue = headers[headerName];
28686 this.xhr.setRequestHeader(headerName, headerValue);
28692 this.xhr.onload = function()
28694 _this.xhrOnLoad(_this.xhr);
28697 this.xhr.onerror = function()
28699 _this.xhrOnError(_this.xhr);
28702 var formData = new FormData();
28704 formData.append('returnHTML', 'NO');
28707 formData.append('crop', crop);
28710 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28711 formData.append(this.paramName, file, file.name);
28714 if(typeof(file.filename) != 'undefined'){
28715 formData.append('filename', file.filename);
28718 if(typeof(file.mimetype) != 'undefined'){
28719 formData.append('mimetype', file.mimetype);
28722 if(this.fireEvent('arrange', this, formData) != false){
28723 this.xhr.send(formData);
28727 xhrOnLoad : function(xhr)
28730 this.maskEl.unmask();
28733 if (xhr.readyState !== 4) {
28734 this.fireEvent('exception', this, xhr);
28738 var response = Roo.decode(xhr.responseText);
28740 if(!response.success){
28741 this.fireEvent('exception', this, xhr);
28745 var response = Roo.decode(xhr.responseText);
28747 this.fireEvent('upload', this, response);
28751 xhrOnError : function()
28754 this.maskEl.unmask();
28757 Roo.log('xhr on error');
28759 var response = Roo.decode(xhr.responseText);
28765 prepare : function(file)
28768 this.maskEl.mask(this.loadingText);
28774 if(typeof(file) === 'string'){
28775 this.loadCanvas(file);
28779 if(!file || !this.urlAPI){
28784 this.cropType = file.type;
28788 if(this.fireEvent('prepare', this, this.file) != false){
28790 var reader = new FileReader();
28792 reader.onload = function (e) {
28793 if (e.target.error) {
28794 Roo.log(e.target.error);
28798 var buffer = e.target.result,
28799 dataView = new DataView(buffer),
28801 maxOffset = dataView.byteLength - 4,
28805 if (dataView.getUint16(0) === 0xffd8) {
28806 while (offset < maxOffset) {
28807 markerBytes = dataView.getUint16(offset);
28809 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28810 markerLength = dataView.getUint16(offset + 2) + 2;
28811 if (offset + markerLength > dataView.byteLength) {
28812 Roo.log('Invalid meta data: Invalid segment size.');
28816 if(markerBytes == 0xffe1){
28817 _this.parseExifData(
28824 offset += markerLength;
28834 var url = _this.urlAPI.createObjectURL(_this.file);
28836 _this.loadCanvas(url);
28841 reader.readAsArrayBuffer(this.file);
28847 parseExifData : function(dataView, offset, length)
28849 var tiffOffset = offset + 10,
28853 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28854 // No Exif data, might be XMP data instead
28858 // Check for the ASCII code for "Exif" (0x45786966):
28859 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28860 // No Exif data, might be XMP data instead
28863 if (tiffOffset + 8 > dataView.byteLength) {
28864 Roo.log('Invalid Exif data: Invalid segment size.');
28867 // Check for the two null bytes:
28868 if (dataView.getUint16(offset + 8) !== 0x0000) {
28869 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28872 // Check the byte alignment:
28873 switch (dataView.getUint16(tiffOffset)) {
28875 littleEndian = true;
28878 littleEndian = false;
28881 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28884 // Check for the TIFF tag marker (0x002A):
28885 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28886 Roo.log('Invalid Exif data: Missing TIFF marker.');
28889 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28890 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28892 this.parseExifTags(
28895 tiffOffset + dirOffset,
28900 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28905 if (dirOffset + 6 > dataView.byteLength) {
28906 Roo.log('Invalid Exif data: Invalid directory offset.');
28909 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28910 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28911 if (dirEndOffset + 4 > dataView.byteLength) {
28912 Roo.log('Invalid Exif data: Invalid directory size.');
28915 for (i = 0; i < tagsNumber; i += 1) {
28919 dirOffset + 2 + 12 * i, // tag offset
28923 // Return the offset to the next directory:
28924 return dataView.getUint32(dirEndOffset, littleEndian);
28927 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28929 var tag = dataView.getUint16(offset, littleEndian);
28931 this.exif[tag] = this.getExifValue(
28935 dataView.getUint16(offset + 2, littleEndian), // tag type
28936 dataView.getUint32(offset + 4, littleEndian), // tag length
28941 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28943 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28952 Roo.log('Invalid Exif data: Invalid tag type.');
28956 tagSize = tagType.size * length;
28957 // Determine if the value is contained in the dataOffset bytes,
28958 // or if the value at the dataOffset is a pointer to the actual data:
28959 dataOffset = tagSize > 4 ?
28960 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28961 if (dataOffset + tagSize > dataView.byteLength) {
28962 Roo.log('Invalid Exif data: Invalid data offset.');
28965 if (length === 1) {
28966 return tagType.getValue(dataView, dataOffset, littleEndian);
28969 for (i = 0; i < length; i += 1) {
28970 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28973 if (tagType.ascii) {
28975 // Concatenate the chars:
28976 for (i = 0; i < values.length; i += 1) {
28978 // Ignore the terminating NULL byte(s):
28979 if (c === '\u0000') {
28991 Roo.apply(Roo.bootstrap.UploadCropbox, {
28993 'Orientation': 0x0112
28997 1: 0, //'top-left',
28999 3: 180, //'bottom-right',
29000 // 4: 'bottom-left',
29002 6: 90, //'right-top',
29003 // 7: 'right-bottom',
29004 8: 270 //'left-bottom'
29008 // byte, 8-bit unsigned int:
29010 getValue: function (dataView, dataOffset) {
29011 return dataView.getUint8(dataOffset);
29015 // ascii, 8-bit byte:
29017 getValue: function (dataView, dataOffset) {
29018 return String.fromCharCode(dataView.getUint8(dataOffset));
29023 // short, 16 bit int:
29025 getValue: function (dataView, dataOffset, littleEndian) {
29026 return dataView.getUint16(dataOffset, littleEndian);
29030 // long, 32 bit int:
29032 getValue: function (dataView, dataOffset, littleEndian) {
29033 return dataView.getUint32(dataOffset, littleEndian);
29037 // rational = two long values, first is numerator, second is denominator:
29039 getValue: function (dataView, dataOffset, littleEndian) {
29040 return dataView.getUint32(dataOffset, littleEndian) /
29041 dataView.getUint32(dataOffset + 4, littleEndian);
29045 // slong, 32 bit signed int:
29047 getValue: function (dataView, dataOffset, littleEndian) {
29048 return dataView.getInt32(dataOffset, littleEndian);
29052 // srational, two slongs, first is numerator, second is denominator:
29054 getValue: function (dataView, dataOffset, littleEndian) {
29055 return dataView.getInt32(dataOffset, littleEndian) /
29056 dataView.getInt32(dataOffset + 4, littleEndian);
29066 cls : 'btn-group roo-upload-cropbox-rotate-left',
29067 action : 'rotate-left',
29071 cls : 'btn btn-default',
29072 html : '<i class="fa fa-undo"></i>'
29078 cls : 'btn-group roo-upload-cropbox-picture',
29079 action : 'picture',
29083 cls : 'btn btn-default',
29084 html : '<i class="fa fa-picture-o"></i>'
29090 cls : 'btn-group roo-upload-cropbox-rotate-right',
29091 action : 'rotate-right',
29095 cls : 'btn btn-default',
29096 html : '<i class="fa fa-repeat"></i>'
29104 cls : 'btn-group roo-upload-cropbox-rotate-left',
29105 action : 'rotate-left',
29109 cls : 'btn btn-default',
29110 html : '<i class="fa fa-undo"></i>'
29116 cls : 'btn-group roo-upload-cropbox-download',
29117 action : 'download',
29121 cls : 'btn btn-default',
29122 html : '<i class="fa fa-download"></i>'
29128 cls : 'btn-group roo-upload-cropbox-crop',
29133 cls : 'btn btn-default',
29134 html : '<i class="fa fa-crop"></i>'
29140 cls : 'btn-group roo-upload-cropbox-trash',
29145 cls : 'btn btn-default',
29146 html : '<i class="fa fa-trash"></i>'
29152 cls : 'btn-group roo-upload-cropbox-rotate-right',
29153 action : 'rotate-right',
29157 cls : 'btn btn-default',
29158 html : '<i class="fa fa-repeat"></i>'
29166 cls : 'btn-group roo-upload-cropbox-rotate-left',
29167 action : 'rotate-left',
29171 cls : 'btn btn-default',
29172 html : '<i class="fa fa-undo"></i>'
29178 cls : 'btn-group roo-upload-cropbox-rotate-right',
29179 action : 'rotate-right',
29183 cls : 'btn btn-default',
29184 html : '<i class="fa fa-repeat"></i>'
29197 * @class Roo.bootstrap.DocumentManager
29198 * @extends Roo.bootstrap.Component
29199 * Bootstrap DocumentManager class
29200 * @cfg {String} paramName default 'imageUpload'
29201 * @cfg {String} toolTipName default 'filename'
29202 * @cfg {String} method default POST
29203 * @cfg {String} url action url
29204 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29205 * @cfg {Boolean} multiple multiple upload default true
29206 * @cfg {Number} thumbSize default 300
29207 * @cfg {String} fieldLabel
29208 * @cfg {Number} labelWidth default 4
29209 * @cfg {String} labelAlign (left|top) default left
29210 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29211 * @cfg {Number} labellg set the width of label (1-12)
29212 * @cfg {Number} labelmd set the width of label (1-12)
29213 * @cfg {Number} labelsm set the width of label (1-12)
29214 * @cfg {Number} labelxs set the width of label (1-12)
29217 * Create a new DocumentManager
29218 * @param {Object} config The config object
29221 Roo.bootstrap.DocumentManager = function(config){
29222 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29225 this.delegates = [];
29230 * Fire when initial the DocumentManager
29231 * @param {Roo.bootstrap.DocumentManager} this
29236 * inspect selected file
29237 * @param {Roo.bootstrap.DocumentManager} this
29238 * @param {File} file
29243 * Fire when xhr load exception
29244 * @param {Roo.bootstrap.DocumentManager} this
29245 * @param {XMLHttpRequest} xhr
29247 "exception" : true,
29249 * @event afterupload
29250 * Fire when xhr load exception
29251 * @param {Roo.bootstrap.DocumentManager} this
29252 * @param {XMLHttpRequest} xhr
29254 "afterupload" : true,
29257 * prepare the form data
29258 * @param {Roo.bootstrap.DocumentManager} this
29259 * @param {Object} formData
29264 * Fire when remove the file
29265 * @param {Roo.bootstrap.DocumentManager} this
29266 * @param {Object} file
29271 * Fire after refresh the file
29272 * @param {Roo.bootstrap.DocumentManager} this
29277 * Fire after click the image
29278 * @param {Roo.bootstrap.DocumentManager} this
29279 * @param {Object} file
29284 * Fire when upload a image and editable set to true
29285 * @param {Roo.bootstrap.DocumentManager} this
29286 * @param {Object} file
29290 * @event beforeselectfile
29291 * Fire before select file
29292 * @param {Roo.bootstrap.DocumentManager} this
29294 "beforeselectfile" : true,
29297 * Fire before process file
29298 * @param {Roo.bootstrap.DocumentManager} this
29299 * @param {Object} file
29303 * @event previewrendered
29304 * Fire when preview rendered
29305 * @param {Roo.bootstrap.DocumentManager} this
29306 * @param {Object} file
29308 "previewrendered" : true,
29311 "previewResize" : true
29316 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
29325 paramName : 'imageUpload',
29326 toolTipName : 'filename',
29329 labelAlign : 'left',
29339 getAutoCreate : function()
29341 var managerWidget = {
29343 cls : 'roo-document-manager',
29347 cls : 'roo-document-manager-selector',
29352 cls : 'roo-document-manager-uploader',
29356 cls : 'roo-document-manager-upload-btn',
29357 html : '<i class="fa fa-plus"></i>'
29368 cls : 'column col-md-12',
29373 if(this.fieldLabel.length){
29378 cls : 'column col-md-12',
29379 html : this.fieldLabel
29383 cls : 'column col-md-12',
29388 if(this.labelAlign == 'left'){
29393 html : this.fieldLabel
29402 if(this.labelWidth > 12){
29403 content[0].style = "width: " + this.labelWidth + 'px';
29406 if(this.labelWidth < 13 && this.labelmd == 0){
29407 this.labelmd = this.labelWidth;
29410 if(this.labellg > 0){
29411 content[0].cls += ' col-lg-' + this.labellg;
29412 content[1].cls += ' col-lg-' + (12 - this.labellg);
29415 if(this.labelmd > 0){
29416 content[0].cls += ' col-md-' + this.labelmd;
29417 content[1].cls += ' col-md-' + (12 - this.labelmd);
29420 if(this.labelsm > 0){
29421 content[0].cls += ' col-sm-' + this.labelsm;
29422 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29425 if(this.labelxs > 0){
29426 content[0].cls += ' col-xs-' + this.labelxs;
29427 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29435 cls : 'row clearfix',
29443 initEvents : function()
29445 this.managerEl = this.el.select('.roo-document-manager', true).first();
29446 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29448 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29449 this.selectorEl.hide();
29452 this.selectorEl.attr('multiple', 'multiple');
29455 this.selectorEl.on('change', this.onFileSelected, this);
29457 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29458 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29460 this.uploader.on('click', this.onUploaderClick, this);
29462 this.renderProgressDialog();
29466 window.addEventListener("resize", function() { _this.refresh(); } );
29468 this.fireEvent('initial', this);
29471 renderProgressDialog : function()
29475 this.progressDialog = new Roo.bootstrap.Modal({
29476 cls : 'roo-document-manager-progress-dialog',
29477 allow_close : false,
29488 btnclick : function() {
29489 _this.uploadCancel();
29495 this.progressDialog.render(Roo.get(document.body));
29497 this.progress = new Roo.bootstrap.Progress({
29498 cls : 'roo-document-manager-progress',
29503 this.progress.render(this.progressDialog.getChildContainer());
29505 this.progressBar = new Roo.bootstrap.ProgressBar({
29506 cls : 'roo-document-manager-progress-bar',
29509 aria_valuemax : 12,
29513 this.progressBar.render(this.progress.getChildContainer());
29516 onUploaderClick : function(e)
29518 e.preventDefault();
29520 if(this.fireEvent('beforeselectfile', this) != false){
29521 this.selectorEl.dom.click();
29526 onFileSelected : function(e)
29528 e.preventDefault();
29530 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29534 Roo.each(this.selectorEl.dom.files, function(file){
29535 if(this.fireEvent('inspect', this, file) != false){
29536 this.files.push(file);
29546 this.selectorEl.dom.value = '';
29548 if(!this.files || !this.files.length){
29552 if(this.boxes > 0 && this.files.length > this.boxes){
29553 this.files = this.files.slice(0, this.boxes);
29556 this.uploader.show();
29558 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29559 this.uploader.hide();
29568 Roo.each(this.files, function(file){
29570 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29571 var f = this.renderPreview(file);
29576 if(file.type.indexOf('image') != -1){
29577 this.delegates.push(
29579 _this.process(file);
29580 }).createDelegate(this)
29588 _this.process(file);
29589 }).createDelegate(this)
29594 this.files = files;
29596 this.delegates = this.delegates.concat(docs);
29598 if(!this.delegates.length){
29603 this.progressBar.aria_valuemax = this.delegates.length;
29610 arrange : function()
29612 if(!this.delegates.length){
29613 this.progressDialog.hide();
29618 var delegate = this.delegates.shift();
29620 this.progressDialog.show();
29622 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29624 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29629 refresh : function()
29631 this.uploader.show();
29633 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29634 this.uploader.hide();
29637 Roo.isTouch ? this.closable(false) : this.closable(true);
29639 this.fireEvent('refresh', this);
29642 onRemove : function(e, el, o)
29644 e.preventDefault();
29646 this.fireEvent('remove', this, o);
29650 remove : function(o)
29654 Roo.each(this.files, function(file){
29655 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29664 this.files = files;
29671 Roo.each(this.files, function(file){
29676 file.target.remove();
29685 onClick : function(e, el, o)
29687 e.preventDefault();
29689 this.fireEvent('click', this, o);
29693 closable : function(closable)
29695 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29697 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29709 xhrOnLoad : function(xhr)
29711 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29715 if (xhr.readyState !== 4) {
29717 this.fireEvent('exception', this, xhr);
29721 var response = Roo.decode(xhr.responseText);
29723 if(!response.success){
29725 this.fireEvent('exception', this, xhr);
29729 var file = this.renderPreview(response.data);
29731 this.files.push(file);
29735 this.fireEvent('afterupload', this, xhr);
29739 xhrOnError : function(xhr)
29741 Roo.log('xhr on error');
29743 var response = Roo.decode(xhr.responseText);
29750 process : function(file)
29752 if(this.fireEvent('process', this, file) !== false){
29753 if(this.editable && file.type.indexOf('image') != -1){
29754 this.fireEvent('edit', this, file);
29758 this.uploadStart(file, false);
29765 uploadStart : function(file, crop)
29767 this.xhr = new XMLHttpRequest();
29769 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29774 file.xhr = this.xhr;
29776 this.managerEl.createChild({
29778 cls : 'roo-document-manager-loading',
29782 tooltip : file.name,
29783 cls : 'roo-document-manager-thumb',
29784 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29790 this.xhr.open(this.method, this.url, true);
29793 "Accept": "application/json",
29794 "Cache-Control": "no-cache",
29795 "X-Requested-With": "XMLHttpRequest"
29798 for (var headerName in headers) {
29799 var headerValue = headers[headerName];
29801 this.xhr.setRequestHeader(headerName, headerValue);
29807 this.xhr.onload = function()
29809 _this.xhrOnLoad(_this.xhr);
29812 this.xhr.onerror = function()
29814 _this.xhrOnError(_this.xhr);
29817 var formData = new FormData();
29819 formData.append('returnHTML', 'NO');
29822 formData.append('crop', crop);
29825 formData.append(this.paramName, file, file.name);
29832 if(this.fireEvent('prepare', this, formData, options) != false){
29834 if(options.manually){
29838 this.xhr.send(formData);
29842 this.uploadCancel();
29845 uploadCancel : function()
29851 this.delegates = [];
29853 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29860 renderPreview : function(file)
29862 if(typeof(file.target) != 'undefined' && file.target){
29866 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29868 var previewEl = this.managerEl.createChild({
29870 cls : 'roo-document-manager-preview',
29874 tooltip : file[this.toolTipName],
29875 cls : 'roo-document-manager-thumb',
29876 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29881 html : '<i class="fa fa-times-circle"></i>'
29886 var close = previewEl.select('button.close', true).first();
29888 close.on('click', this.onRemove, this, file);
29890 file.target = previewEl;
29892 var image = previewEl.select('img', true).first();
29896 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29898 image.on('click', this.onClick, this, file);
29900 this.fireEvent('previewrendered', this, file);
29906 onPreviewLoad : function(file, image)
29908 if(typeof(file.target) == 'undefined' || !file.target){
29912 var width = image.dom.naturalWidth || image.dom.width;
29913 var height = image.dom.naturalHeight || image.dom.height;
29915 if(!this.previewResize) {
29919 if(width > height){
29920 file.target.addClass('wide');
29924 file.target.addClass('tall');
29929 uploadFromSource : function(file, crop)
29931 this.xhr = new XMLHttpRequest();
29933 this.managerEl.createChild({
29935 cls : 'roo-document-manager-loading',
29939 tooltip : file.name,
29940 cls : 'roo-document-manager-thumb',
29941 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29947 this.xhr.open(this.method, this.url, true);
29950 "Accept": "application/json",
29951 "Cache-Control": "no-cache",
29952 "X-Requested-With": "XMLHttpRequest"
29955 for (var headerName in headers) {
29956 var headerValue = headers[headerName];
29958 this.xhr.setRequestHeader(headerName, headerValue);
29964 this.xhr.onload = function()
29966 _this.xhrOnLoad(_this.xhr);
29969 this.xhr.onerror = function()
29971 _this.xhrOnError(_this.xhr);
29974 var formData = new FormData();
29976 formData.append('returnHTML', 'NO');
29978 formData.append('crop', crop);
29980 if(typeof(file.filename) != 'undefined'){
29981 formData.append('filename', file.filename);
29984 if(typeof(file.mimetype) != 'undefined'){
29985 formData.append('mimetype', file.mimetype);
29990 if(this.fireEvent('prepare', this, formData) != false){
29991 this.xhr.send(formData);
30001 * @class Roo.bootstrap.DocumentViewer
30002 * @extends Roo.bootstrap.Component
30003 * Bootstrap DocumentViewer class
30004 * @cfg {Boolean} showDownload (true|false) show download button (default true)
30005 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30008 * Create a new DocumentViewer
30009 * @param {Object} config The config object
30012 Roo.bootstrap.DocumentViewer = function(config){
30013 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30018 * Fire after initEvent
30019 * @param {Roo.bootstrap.DocumentViewer} this
30025 * @param {Roo.bootstrap.DocumentViewer} this
30030 * Fire after download button
30031 * @param {Roo.bootstrap.DocumentViewer} this
30036 * Fire after trash button
30037 * @param {Roo.bootstrap.DocumentViewer} this
30044 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
30046 showDownload : true,
30050 getAutoCreate : function()
30054 cls : 'roo-document-viewer',
30058 cls : 'roo-document-viewer-body',
30062 cls : 'roo-document-viewer-thumb',
30066 cls : 'roo-document-viewer-image'
30074 cls : 'roo-document-viewer-footer',
30077 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30081 cls : 'btn-group roo-document-viewer-download',
30085 cls : 'btn btn-default',
30086 html : '<i class="fa fa-download"></i>'
30092 cls : 'btn-group roo-document-viewer-trash',
30096 cls : 'btn btn-default',
30097 html : '<i class="fa fa-trash"></i>'
30110 initEvents : function()
30112 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30113 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30115 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30116 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30118 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30119 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30121 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30122 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30124 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30125 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30127 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30128 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30130 this.bodyEl.on('click', this.onClick, this);
30131 this.downloadBtn.on('click', this.onDownload, this);
30132 this.trashBtn.on('click', this.onTrash, this);
30134 this.downloadBtn.hide();
30135 this.trashBtn.hide();
30137 if(this.showDownload){
30138 this.downloadBtn.show();
30141 if(this.showTrash){
30142 this.trashBtn.show();
30145 if(!this.showDownload && !this.showTrash) {
30146 this.footerEl.hide();
30151 initial : function()
30153 this.fireEvent('initial', this);
30157 onClick : function(e)
30159 e.preventDefault();
30161 this.fireEvent('click', this);
30164 onDownload : function(e)
30166 e.preventDefault();
30168 this.fireEvent('download', this);
30171 onTrash : function(e)
30173 e.preventDefault();
30175 this.fireEvent('trash', this);
30187 * @class Roo.bootstrap.NavProgressBar
30188 * @extends Roo.bootstrap.Component
30189 * Bootstrap NavProgressBar class
30192 * Create a new nav progress bar
30193 * @param {Object} config The config object
30196 Roo.bootstrap.NavProgressBar = function(config){
30197 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30199 this.bullets = this.bullets || [];
30201 // Roo.bootstrap.NavProgressBar.register(this);
30205 * Fires when the active item changes
30206 * @param {Roo.bootstrap.NavProgressBar} this
30207 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30208 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
30215 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
30220 getAutoCreate : function()
30222 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30226 cls : 'roo-navigation-bar-group',
30230 cls : 'roo-navigation-top-bar'
30234 cls : 'roo-navigation-bullets-bar',
30238 cls : 'roo-navigation-bar'
30245 cls : 'roo-navigation-bottom-bar'
30255 initEvents: function()
30260 onRender : function(ct, position)
30262 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30264 if(this.bullets.length){
30265 Roo.each(this.bullets, function(b){
30274 addItem : function(cfg)
30276 var item = new Roo.bootstrap.NavProgressItem(cfg);
30278 item.parentId = this.id;
30279 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30282 var top = new Roo.bootstrap.Element({
30284 cls : 'roo-navigation-bar-text'
30287 var bottom = new Roo.bootstrap.Element({
30289 cls : 'roo-navigation-bar-text'
30292 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30293 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30295 var topText = new Roo.bootstrap.Element({
30297 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30300 var bottomText = new Roo.bootstrap.Element({
30302 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30305 topText.onRender(top.el, null);
30306 bottomText.onRender(bottom.el, null);
30309 item.bottomEl = bottom;
30312 this.barItems.push(item);
30317 getActive : function()
30319 var active = false;
30321 Roo.each(this.barItems, function(v){
30323 if (!v.isActive()) {
30335 setActiveItem : function(item)
30339 Roo.each(this.barItems, function(v){
30340 if (v.rid == item.rid) {
30344 if (v.isActive()) {
30345 v.setActive(false);
30350 item.setActive(true);
30352 this.fireEvent('changed', this, item, prev);
30355 getBarItem: function(rid)
30359 Roo.each(this.barItems, function(e) {
30360 if (e.rid != rid) {
30371 indexOfItem : function(item)
30375 Roo.each(this.barItems, function(v, i){
30377 if (v.rid != item.rid) {
30388 setActiveNext : function()
30390 var i = this.indexOfItem(this.getActive());
30392 if (i > this.barItems.length) {
30396 this.setActiveItem(this.barItems[i+1]);
30399 setActivePrev : function()
30401 var i = this.indexOfItem(this.getActive());
30407 this.setActiveItem(this.barItems[i-1]);
30410 format : function()
30412 if(!this.barItems.length){
30416 var width = 100 / this.barItems.length;
30418 Roo.each(this.barItems, function(i){
30419 i.el.setStyle('width', width + '%');
30420 i.topEl.el.setStyle('width', width + '%');
30421 i.bottomEl.el.setStyle('width', width + '%');
30430 * Nav Progress Item
30435 * @class Roo.bootstrap.NavProgressItem
30436 * @extends Roo.bootstrap.Component
30437 * Bootstrap NavProgressItem class
30438 * @cfg {String} rid the reference id
30439 * @cfg {Boolean} active (true|false) Is item active default false
30440 * @cfg {Boolean} disabled (true|false) Is item active default false
30441 * @cfg {String} html
30442 * @cfg {String} position (top|bottom) text position default bottom
30443 * @cfg {String} icon show icon instead of number
30446 * Create a new NavProgressItem
30447 * @param {Object} config The config object
30449 Roo.bootstrap.NavProgressItem = function(config){
30450 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30455 * The raw click event for the entire grid.
30456 * @param {Roo.bootstrap.NavProgressItem} this
30457 * @param {Roo.EventObject} e
30464 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30470 position : 'bottom',
30473 getAutoCreate : function()
30475 var iconCls = 'roo-navigation-bar-item-icon';
30477 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30481 cls: 'roo-navigation-bar-item',
30491 cfg.cls += ' active';
30494 cfg.cls += ' disabled';
30500 disable : function()
30502 this.setDisabled(true);
30505 enable : function()
30507 this.setDisabled(false);
30510 initEvents: function()
30512 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30514 this.iconEl.on('click', this.onClick, this);
30517 onClick : function(e)
30519 e.preventDefault();
30525 if(this.fireEvent('click', this, e) === false){
30529 this.parent().setActiveItem(this);
30532 isActive: function ()
30534 return this.active;
30537 setActive : function(state)
30539 if(this.active == state){
30543 this.active = state;
30546 this.el.addClass('active');
30550 this.el.removeClass('active');
30555 setDisabled : function(state)
30557 if(this.disabled == state){
30561 this.disabled = state;
30564 this.el.addClass('disabled');
30568 this.el.removeClass('disabled');
30571 tooltipEl : function()
30573 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30586 * @class Roo.bootstrap.FieldLabel
30587 * @extends Roo.bootstrap.Component
30588 * Bootstrap FieldLabel class
30589 * @cfg {String} html contents of the element
30590 * @cfg {String} tag tag of the element default label
30591 * @cfg {String} cls class of the element
30592 * @cfg {String} target label target
30593 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30594 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30595 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30596 * @cfg {String} iconTooltip default "This field is required"
30597 * @cfg {String} indicatorpos (left|right) default left
30600 * Create a new FieldLabel
30601 * @param {Object} config The config object
30604 Roo.bootstrap.FieldLabel = function(config){
30605 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30610 * Fires after the field has been marked as invalid.
30611 * @param {Roo.form.FieldLabel} this
30612 * @param {String} msg The validation message
30617 * Fires after the field has been validated with no errors.
30618 * @param {Roo.form.FieldLabel} this
30624 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30631 invalidClass : 'has-warning',
30632 validClass : 'has-success',
30633 iconTooltip : 'This field is required',
30634 indicatorpos : 'left',
30636 getAutoCreate : function(){
30639 if (!this.allowBlank) {
30645 cls : 'roo-bootstrap-field-label ' + this.cls,
30650 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30651 tooltip : this.iconTooltip
30660 if(this.indicatorpos == 'right'){
30663 cls : 'roo-bootstrap-field-label ' + this.cls,
30672 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30673 tooltip : this.iconTooltip
30682 initEvents: function()
30684 Roo.bootstrap.Element.superclass.initEvents.call(this);
30686 this.indicator = this.indicatorEl();
30688 if(this.indicator){
30689 this.indicator.removeClass('visible');
30690 this.indicator.addClass('invisible');
30693 Roo.bootstrap.FieldLabel.register(this);
30696 indicatorEl : function()
30698 var indicator = this.el.select('i.roo-required-indicator',true).first();
30709 * Mark this field as valid
30711 markValid : function()
30713 if(this.indicator){
30714 this.indicator.removeClass('visible');
30715 this.indicator.addClass('invisible');
30717 if (Roo.bootstrap.version == 3) {
30718 this.el.removeClass(this.invalidClass);
30719 this.el.addClass(this.validClass);
30721 this.el.removeClass('is-invalid');
30722 this.el.addClass('is-valid');
30726 this.fireEvent('valid', this);
30730 * Mark this field as invalid
30731 * @param {String} msg The validation message
30733 markInvalid : function(msg)
30735 if(this.indicator){
30736 this.indicator.removeClass('invisible');
30737 this.indicator.addClass('visible');
30739 if (Roo.bootstrap.version == 3) {
30740 this.el.removeClass(this.validClass);
30741 this.el.addClass(this.invalidClass);
30743 this.el.removeClass('is-valid');
30744 this.el.addClass('is-invalid');
30748 this.fireEvent('invalid', this, msg);
30754 Roo.apply(Roo.bootstrap.FieldLabel, {
30759 * register a FieldLabel Group
30760 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30762 register : function(label)
30764 if(this.groups.hasOwnProperty(label.target)){
30768 this.groups[label.target] = label;
30772 * fetch a FieldLabel Group based on the target
30773 * @param {string} target
30774 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30776 get: function(target) {
30777 if (typeof(this.groups[target]) == 'undefined') {
30781 return this.groups[target] ;
30790 * page DateSplitField.
30796 * @class Roo.bootstrap.DateSplitField
30797 * @extends Roo.bootstrap.Component
30798 * Bootstrap DateSplitField class
30799 * @cfg {string} fieldLabel - the label associated
30800 * @cfg {Number} labelWidth set the width of label (0-12)
30801 * @cfg {String} labelAlign (top|left)
30802 * @cfg {Boolean} dayAllowBlank (true|false) default false
30803 * @cfg {Boolean} monthAllowBlank (true|false) default false
30804 * @cfg {Boolean} yearAllowBlank (true|false) default false
30805 * @cfg {string} dayPlaceholder
30806 * @cfg {string} monthPlaceholder
30807 * @cfg {string} yearPlaceholder
30808 * @cfg {string} dayFormat default 'd'
30809 * @cfg {string} monthFormat default 'm'
30810 * @cfg {string} yearFormat default 'Y'
30811 * @cfg {Number} labellg set the width of label (1-12)
30812 * @cfg {Number} labelmd set the width of label (1-12)
30813 * @cfg {Number} labelsm set the width of label (1-12)
30814 * @cfg {Number} labelxs set the width of label (1-12)
30818 * Create a new DateSplitField
30819 * @param {Object} config The config object
30822 Roo.bootstrap.DateSplitField = function(config){
30823 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30829 * getting the data of years
30830 * @param {Roo.bootstrap.DateSplitField} this
30831 * @param {Object} years
30836 * getting the data of days
30837 * @param {Roo.bootstrap.DateSplitField} this
30838 * @param {Object} days
30843 * Fires after the field has been marked as invalid.
30844 * @param {Roo.form.Field} this
30845 * @param {String} msg The validation message
30850 * Fires after the field has been validated with no errors.
30851 * @param {Roo.form.Field} this
30857 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30860 labelAlign : 'top',
30862 dayAllowBlank : false,
30863 monthAllowBlank : false,
30864 yearAllowBlank : false,
30865 dayPlaceholder : '',
30866 monthPlaceholder : '',
30867 yearPlaceholder : '',
30871 isFormField : true,
30877 getAutoCreate : function()
30881 cls : 'row roo-date-split-field-group',
30886 cls : 'form-hidden-field roo-date-split-field-group-value',
30892 var labelCls = 'col-md-12';
30893 var contentCls = 'col-md-4';
30895 if(this.fieldLabel){
30899 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30903 html : this.fieldLabel
30908 if(this.labelAlign == 'left'){
30910 if(this.labelWidth > 12){
30911 label.style = "width: " + this.labelWidth + 'px';
30914 if(this.labelWidth < 13 && this.labelmd == 0){
30915 this.labelmd = this.labelWidth;
30918 if(this.labellg > 0){
30919 labelCls = ' col-lg-' + this.labellg;
30920 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30923 if(this.labelmd > 0){
30924 labelCls = ' col-md-' + this.labelmd;
30925 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30928 if(this.labelsm > 0){
30929 labelCls = ' col-sm-' + this.labelsm;
30930 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30933 if(this.labelxs > 0){
30934 labelCls = ' col-xs-' + this.labelxs;
30935 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30939 label.cls += ' ' + labelCls;
30941 cfg.cn.push(label);
30944 Roo.each(['day', 'month', 'year'], function(t){
30947 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30954 inputEl: function ()
30956 return this.el.select('.roo-date-split-field-group-value', true).first();
30959 onRender : function(ct, position)
30963 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30965 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30967 this.dayField = new Roo.bootstrap.ComboBox({
30968 allowBlank : this.dayAllowBlank,
30969 alwaysQuery : true,
30970 displayField : 'value',
30973 forceSelection : true,
30975 placeholder : this.dayPlaceholder,
30976 selectOnFocus : true,
30977 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30978 triggerAction : 'all',
30980 valueField : 'value',
30981 store : new Roo.data.SimpleStore({
30982 data : (function() {
30984 _this.fireEvent('days', _this, days);
30987 fields : [ 'value' ]
30990 select : function (_self, record, index)
30992 _this.setValue(_this.getValue());
30997 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30999 this.monthField = new Roo.bootstrap.MonthField({
31000 after : '<i class=\"fa fa-calendar\"></i>',
31001 allowBlank : this.monthAllowBlank,
31002 placeholder : this.monthPlaceholder,
31005 render : function (_self)
31007 this.el.select('span.input-group-addon', true).first().on('click', function(e){
31008 e.preventDefault();
31012 select : function (_self, oldvalue, newvalue)
31014 _this.setValue(_this.getValue());
31019 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31021 this.yearField = new Roo.bootstrap.ComboBox({
31022 allowBlank : this.yearAllowBlank,
31023 alwaysQuery : true,
31024 displayField : 'value',
31027 forceSelection : true,
31029 placeholder : this.yearPlaceholder,
31030 selectOnFocus : true,
31031 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31032 triggerAction : 'all',
31034 valueField : 'value',
31035 store : new Roo.data.SimpleStore({
31036 data : (function() {
31038 _this.fireEvent('years', _this, years);
31041 fields : [ 'value' ]
31044 select : function (_self, record, index)
31046 _this.setValue(_this.getValue());
31051 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31054 setValue : function(v, format)
31056 this.inputEl.dom.value = v;
31058 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31060 var d = Date.parseDate(v, f);
31067 this.setDay(d.format(this.dayFormat));
31068 this.setMonth(d.format(this.monthFormat));
31069 this.setYear(d.format(this.yearFormat));
31076 setDay : function(v)
31078 this.dayField.setValue(v);
31079 this.inputEl.dom.value = this.getValue();
31084 setMonth : function(v)
31086 this.monthField.setValue(v, true);
31087 this.inputEl.dom.value = this.getValue();
31092 setYear : function(v)
31094 this.yearField.setValue(v);
31095 this.inputEl.dom.value = this.getValue();
31100 getDay : function()
31102 return this.dayField.getValue();
31105 getMonth : function()
31107 return this.monthField.getValue();
31110 getYear : function()
31112 return this.yearField.getValue();
31115 getValue : function()
31117 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31119 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31129 this.inputEl.dom.value = '';
31134 validate : function()
31136 var d = this.dayField.validate();
31137 var m = this.monthField.validate();
31138 var y = this.yearField.validate();
31143 (!this.dayAllowBlank && !d) ||
31144 (!this.monthAllowBlank && !m) ||
31145 (!this.yearAllowBlank && !y)
31150 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31159 this.markInvalid();
31164 markValid : function()
31167 var label = this.el.select('label', true).first();
31168 var icon = this.el.select('i.fa-star', true).first();
31174 this.fireEvent('valid', this);
31178 * Mark this field as invalid
31179 * @param {String} msg The validation message
31181 markInvalid : function(msg)
31184 var label = this.el.select('label', true).first();
31185 var icon = this.el.select('i.fa-star', true).first();
31187 if(label && !icon){
31188 this.el.select('.roo-date-split-field-label', true).createChild({
31190 cls : 'text-danger fa fa-lg fa-star',
31191 tooltip : 'This field is required',
31192 style : 'margin-right:5px;'
31196 this.fireEvent('invalid', this, msg);
31199 clearInvalid : function()
31201 var label = this.el.select('label', true).first();
31202 var icon = this.el.select('i.fa-star', true).first();
31208 this.fireEvent('valid', this);
31211 getName: function()
31221 * http://masonry.desandro.com
31223 * The idea is to render all the bricks based on vertical width...
31225 * The original code extends 'outlayer' - we might need to use that....
31231 * @class Roo.bootstrap.LayoutMasonry
31232 * @extends Roo.bootstrap.Component
31233 * Bootstrap Layout Masonry class
31236 * Create a new Element
31237 * @param {Object} config The config object
31240 Roo.bootstrap.LayoutMasonry = function(config){
31242 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31246 Roo.bootstrap.LayoutMasonry.register(this);
31252 * Fire after layout the items
31253 * @param {Roo.bootstrap.LayoutMasonry} this
31254 * @param {Roo.EventObject} e
31261 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
31264 * @cfg {Boolean} isLayoutInstant = no animation?
31266 isLayoutInstant : false, // needed?
31269 * @cfg {Number} boxWidth width of the columns
31274 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
31279 * @cfg {Number} padWidth padding below box..
31284 * @cfg {Number} gutter gutter width..
31289 * @cfg {Number} maxCols maximum number of columns
31295 * @cfg {Boolean} isAutoInitial defalut true
31297 isAutoInitial : true,
31302 * @cfg {Boolean} isHorizontal defalut false
31304 isHorizontal : false,
31306 currentSize : null,
31312 bricks: null, //CompositeElement
31316 _isLayoutInited : false,
31318 // isAlternative : false, // only use for vertical layout...
31321 * @cfg {Number} alternativePadWidth padding below box..
31323 alternativePadWidth : 50,
31325 selectedBrick : [],
31327 getAutoCreate : function(){
31329 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31333 cls: 'blog-masonary-wrapper ' + this.cls,
31335 cls : 'mas-boxes masonary'
31342 getChildContainer: function( )
31344 if (this.boxesEl) {
31345 return this.boxesEl;
31348 this.boxesEl = this.el.select('.mas-boxes').first();
31350 return this.boxesEl;
31354 initEvents : function()
31358 if(this.isAutoInitial){
31359 Roo.log('hook children rendered');
31360 this.on('childrenrendered', function() {
31361 Roo.log('children rendered');
31367 initial : function()
31369 this.selectedBrick = [];
31371 this.currentSize = this.el.getBox(true);
31373 Roo.EventManager.onWindowResize(this.resize, this);
31375 if(!this.isAutoInitial){
31383 //this.layout.defer(500,this);
31387 resize : function()
31389 var cs = this.el.getBox(true);
31392 this.currentSize.width == cs.width &&
31393 this.currentSize.x == cs.x &&
31394 this.currentSize.height == cs.height &&
31395 this.currentSize.y == cs.y
31397 Roo.log("no change in with or X or Y");
31401 this.currentSize = cs;
31407 layout : function()
31409 this._resetLayout();
31411 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31413 this.layoutItems( isInstant );
31415 this._isLayoutInited = true;
31417 this.fireEvent('layout', this);
31421 _resetLayout : function()
31423 if(this.isHorizontal){
31424 this.horizontalMeasureColumns();
31428 this.verticalMeasureColumns();
31432 verticalMeasureColumns : function()
31434 this.getContainerWidth();
31436 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31437 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31441 var boxWidth = this.boxWidth + this.padWidth;
31443 if(this.containerWidth < this.boxWidth){
31444 boxWidth = this.containerWidth
31447 var containerWidth = this.containerWidth;
31449 var cols = Math.floor(containerWidth / boxWidth);
31451 this.cols = Math.max( cols, 1 );
31453 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31455 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31457 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31459 this.colWidth = boxWidth + avail - this.padWidth;
31461 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31462 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31465 horizontalMeasureColumns : function()
31467 this.getContainerWidth();
31469 var boxWidth = this.boxWidth;
31471 if(this.containerWidth < boxWidth){
31472 boxWidth = this.containerWidth;
31475 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31477 this.el.setHeight(boxWidth);
31481 getContainerWidth : function()
31483 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31486 layoutItems : function( isInstant )
31488 Roo.log(this.bricks);
31490 var items = Roo.apply([], this.bricks);
31492 if(this.isHorizontal){
31493 this._horizontalLayoutItems( items , isInstant );
31497 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31498 // this._verticalAlternativeLayoutItems( items , isInstant );
31502 this._verticalLayoutItems( items , isInstant );
31506 _verticalLayoutItems : function ( items , isInstant)
31508 if ( !items || !items.length ) {
31513 ['xs', 'xs', 'xs', 'tall'],
31514 ['xs', 'xs', 'tall'],
31515 ['xs', 'xs', 'sm'],
31516 ['xs', 'xs', 'xs'],
31522 ['sm', 'xs', 'xs'],
31526 ['tall', 'xs', 'xs', 'xs'],
31527 ['tall', 'xs', 'xs'],
31539 Roo.each(items, function(item, k){
31541 switch (item.size) {
31542 // these layouts take up a full box,
31553 boxes.push([item]);
31576 var filterPattern = function(box, length)
31584 var pattern = box.slice(0, length);
31588 Roo.each(pattern, function(i){
31589 format.push(i.size);
31592 Roo.each(standard, function(s){
31594 if(String(s) != String(format)){
31603 if(!match && length == 1){
31608 filterPattern(box, length - 1);
31612 queue.push(pattern);
31614 box = box.slice(length, box.length);
31616 filterPattern(box, 4);
31622 Roo.each(boxes, function(box, k){
31628 if(box.length == 1){
31633 filterPattern(box, 4);
31637 this._processVerticalLayoutQueue( queue, isInstant );
31641 // _verticalAlternativeLayoutItems : function( items , isInstant )
31643 // if ( !items || !items.length ) {
31647 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31651 _horizontalLayoutItems : function ( items , isInstant)
31653 if ( !items || !items.length || items.length < 3) {
31659 var eItems = items.slice(0, 3);
31661 items = items.slice(3, items.length);
31664 ['xs', 'xs', 'xs', 'wide'],
31665 ['xs', 'xs', 'wide'],
31666 ['xs', 'xs', 'sm'],
31667 ['xs', 'xs', 'xs'],
31673 ['sm', 'xs', 'xs'],
31677 ['wide', 'xs', 'xs', 'xs'],
31678 ['wide', 'xs', 'xs'],
31691 Roo.each(items, function(item, k){
31693 switch (item.size) {
31704 boxes.push([item]);
31728 var filterPattern = function(box, length)
31736 var pattern = box.slice(0, length);
31740 Roo.each(pattern, function(i){
31741 format.push(i.size);
31744 Roo.each(standard, function(s){
31746 if(String(s) != String(format)){
31755 if(!match && length == 1){
31760 filterPattern(box, length - 1);
31764 queue.push(pattern);
31766 box = box.slice(length, box.length);
31768 filterPattern(box, 4);
31774 Roo.each(boxes, function(box, k){
31780 if(box.length == 1){
31785 filterPattern(box, 4);
31792 var pos = this.el.getBox(true);
31796 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31798 var hit_end = false;
31800 Roo.each(queue, function(box){
31804 Roo.each(box, function(b){
31806 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31816 Roo.each(box, function(b){
31818 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31821 mx = Math.max(mx, b.x);
31825 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31829 Roo.each(box, function(b){
31831 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31845 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31848 /** Sets position of item in DOM
31849 * @param {Element} item
31850 * @param {Number} x - horizontal position
31851 * @param {Number} y - vertical position
31852 * @param {Boolean} isInstant - disables transitions
31854 _processVerticalLayoutQueue : function( queue, isInstant )
31856 var pos = this.el.getBox(true);
31861 for (var i = 0; i < this.cols; i++){
31865 Roo.each(queue, function(box, k){
31867 var col = k % this.cols;
31869 Roo.each(box, function(b,kk){
31871 b.el.position('absolute');
31873 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31874 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31876 if(b.size == 'md-left' || b.size == 'md-right'){
31877 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31878 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31881 b.el.setWidth(width);
31882 b.el.setHeight(height);
31884 b.el.select('iframe',true).setSize(width,height);
31888 for (var i = 0; i < this.cols; i++){
31890 if(maxY[i] < maxY[col]){
31895 col = Math.min(col, i);
31899 x = pos.x + col * (this.colWidth + this.padWidth);
31903 var positions = [];
31905 switch (box.length){
31907 positions = this.getVerticalOneBoxColPositions(x, y, box);
31910 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31913 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31916 positions = this.getVerticalFourBoxColPositions(x, y, box);
31922 Roo.each(box, function(b,kk){
31924 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31926 var sz = b.el.getSize();
31928 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31936 for (var i = 0; i < this.cols; i++){
31937 mY = Math.max(mY, maxY[i]);
31940 this.el.setHeight(mY - pos.y);
31944 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31946 // var pos = this.el.getBox(true);
31949 // var maxX = pos.right;
31951 // var maxHeight = 0;
31953 // Roo.each(items, function(item, k){
31957 // item.el.position('absolute');
31959 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31961 // item.el.setWidth(width);
31963 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31965 // item.el.setHeight(height);
31968 // item.el.setXY([x, y], isInstant ? false : true);
31970 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31973 // y = y + height + this.alternativePadWidth;
31975 // maxHeight = maxHeight + height + this.alternativePadWidth;
31979 // this.el.setHeight(maxHeight);
31983 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31985 var pos = this.el.getBox(true);
31990 var maxX = pos.right;
31992 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31994 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31996 Roo.each(queue, function(box, k){
31998 Roo.each(box, function(b, kk){
32000 b.el.position('absolute');
32002 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32003 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32005 if(b.size == 'md-left' || b.size == 'md-right'){
32006 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32007 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32010 b.el.setWidth(width);
32011 b.el.setHeight(height);
32019 var positions = [];
32021 switch (box.length){
32023 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32026 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32029 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32032 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32038 Roo.each(box, function(b,kk){
32040 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32042 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32050 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32052 Roo.each(eItems, function(b,k){
32054 b.size = (k == 0) ? 'sm' : 'xs';
32055 b.x = (k == 0) ? 2 : 1;
32056 b.y = (k == 0) ? 2 : 1;
32058 b.el.position('absolute');
32060 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32062 b.el.setWidth(width);
32064 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32066 b.el.setHeight(height);
32070 var positions = [];
32073 x : maxX - this.unitWidth * 2 - this.gutter,
32078 x : maxX - this.unitWidth,
32079 y : minY + (this.unitWidth + this.gutter) * 2
32083 x : maxX - this.unitWidth * 3 - this.gutter * 2,
32087 Roo.each(eItems, function(b,k){
32089 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32095 getVerticalOneBoxColPositions : function(x, y, box)
32099 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32101 if(box[0].size == 'md-left'){
32105 if(box[0].size == 'md-right'){
32110 x : x + (this.unitWidth + this.gutter) * rand,
32117 getVerticalTwoBoxColPositions : function(x, y, box)
32121 if(box[0].size == 'xs'){
32125 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32129 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32143 x : x + (this.unitWidth + this.gutter) * 2,
32144 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32151 getVerticalThreeBoxColPositions : function(x, y, box)
32155 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32163 x : x + (this.unitWidth + this.gutter) * 1,
32168 x : x + (this.unitWidth + this.gutter) * 2,
32176 if(box[0].size == 'xs' && box[1].size == 'xs'){
32185 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32189 x : x + (this.unitWidth + this.gutter) * 1,
32203 x : x + (this.unitWidth + this.gutter) * 2,
32208 x : x + (this.unitWidth + this.gutter) * 2,
32209 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32216 getVerticalFourBoxColPositions : function(x, y, box)
32220 if(box[0].size == 'xs'){
32229 y : y + (this.unitHeight + this.gutter) * 1
32234 y : y + (this.unitHeight + this.gutter) * 2
32238 x : x + (this.unitWidth + this.gutter) * 1,
32252 x : x + (this.unitWidth + this.gutter) * 2,
32257 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32258 y : y + (this.unitHeight + this.gutter) * 1
32262 x : x + (this.unitWidth + this.gutter) * 2,
32263 y : y + (this.unitWidth + this.gutter) * 2
32270 getHorizontalOneBoxColPositions : function(maxX, minY, box)
32274 if(box[0].size == 'md-left'){
32276 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32283 if(box[0].size == 'md-right'){
32285 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32286 y : minY + (this.unitWidth + this.gutter) * 1
32292 var rand = Math.floor(Math.random() * (4 - box[0].y));
32295 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32296 y : minY + (this.unitWidth + this.gutter) * rand
32303 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32307 if(box[0].size == 'xs'){
32310 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32315 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32316 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32324 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32329 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32330 y : minY + (this.unitWidth + this.gutter) * 2
32337 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32341 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32344 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32349 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32350 y : minY + (this.unitWidth + this.gutter) * 1
32354 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32355 y : minY + (this.unitWidth + this.gutter) * 2
32362 if(box[0].size == 'xs' && box[1].size == 'xs'){
32365 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32370 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32375 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32376 y : minY + (this.unitWidth + this.gutter) * 1
32384 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32389 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32390 y : minY + (this.unitWidth + this.gutter) * 2
32394 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32395 y : minY + (this.unitWidth + this.gutter) * 2
32402 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32406 if(box[0].size == 'xs'){
32409 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32414 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32419 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32424 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32425 y : minY + (this.unitWidth + this.gutter) * 1
32433 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32438 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32439 y : minY + (this.unitWidth + this.gutter) * 2
32443 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32444 y : minY + (this.unitWidth + this.gutter) * 2
32448 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32449 y : minY + (this.unitWidth + this.gutter) * 2
32457 * remove a Masonry Brick
32458 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32460 removeBrick : function(brick_id)
32466 for (var i = 0; i<this.bricks.length; i++) {
32467 if (this.bricks[i].id == brick_id) {
32468 this.bricks.splice(i,1);
32469 this.el.dom.removeChild(Roo.get(brick_id).dom);
32476 * adds a Masonry Brick
32477 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32479 addBrick : function(cfg)
32481 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32482 //this.register(cn);
32483 cn.parentId = this.id;
32484 cn.render(this.el);
32489 * register a Masonry Brick
32490 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32493 register : function(brick)
32495 this.bricks.push(brick);
32496 brick.masonryId = this.id;
32500 * clear all the Masonry Brick
32502 clearAll : function()
32505 //this.getChildContainer().dom.innerHTML = "";
32506 this.el.dom.innerHTML = '';
32509 getSelected : function()
32511 if (!this.selectedBrick) {
32515 return this.selectedBrick;
32519 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32523 * register a Masonry Layout
32524 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32527 register : function(layout)
32529 this.groups[layout.id] = layout;
32532 * fetch a Masonry Layout based on the masonry layout ID
32533 * @param {string} the masonry layout to add
32534 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32537 get: function(layout_id) {
32538 if (typeof(this.groups[layout_id]) == 'undefined') {
32541 return this.groups[layout_id] ;
32553 * http://masonry.desandro.com
32555 * The idea is to render all the bricks based on vertical width...
32557 * The original code extends 'outlayer' - we might need to use that....
32563 * @class Roo.bootstrap.LayoutMasonryAuto
32564 * @extends Roo.bootstrap.Component
32565 * Bootstrap Layout Masonry class
32568 * Create a new Element
32569 * @param {Object} config The config object
32572 Roo.bootstrap.LayoutMasonryAuto = function(config){
32573 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32576 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32579 * @cfg {Boolean} isFitWidth - resize the width..
32581 isFitWidth : false, // options..
32583 * @cfg {Boolean} isOriginLeft = left align?
32585 isOriginLeft : true,
32587 * @cfg {Boolean} isOriginTop = top align?
32589 isOriginTop : false,
32591 * @cfg {Boolean} isLayoutInstant = no animation?
32593 isLayoutInstant : false, // needed?
32595 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32597 isResizingContainer : true,
32599 * @cfg {Number} columnWidth width of the columns
32605 * @cfg {Number} maxCols maximum number of columns
32610 * @cfg {Number} padHeight padding below box..
32616 * @cfg {Boolean} isAutoInitial defalut true
32619 isAutoInitial : true,
32625 initialColumnWidth : 0,
32626 currentSize : null,
32628 colYs : null, // array.
32635 bricks: null, //CompositeElement
32636 cols : 0, // array?
32637 // element : null, // wrapped now this.el
32638 _isLayoutInited : null,
32641 getAutoCreate : function(){
32645 cls: 'blog-masonary-wrapper ' + this.cls,
32647 cls : 'mas-boxes masonary'
32654 getChildContainer: function( )
32656 if (this.boxesEl) {
32657 return this.boxesEl;
32660 this.boxesEl = this.el.select('.mas-boxes').first();
32662 return this.boxesEl;
32666 initEvents : function()
32670 if(this.isAutoInitial){
32671 Roo.log('hook children rendered');
32672 this.on('childrenrendered', function() {
32673 Roo.log('children rendered');
32680 initial : function()
32682 this.reloadItems();
32684 this.currentSize = this.el.getBox(true);
32686 /// was window resize... - let's see if this works..
32687 Roo.EventManager.onWindowResize(this.resize, this);
32689 if(!this.isAutoInitial){
32694 this.layout.defer(500,this);
32697 reloadItems: function()
32699 this.bricks = this.el.select('.masonry-brick', true);
32701 this.bricks.each(function(b) {
32702 //Roo.log(b.getSize());
32703 if (!b.attr('originalwidth')) {
32704 b.attr('originalwidth', b.getSize().width);
32709 Roo.log(this.bricks.elements.length);
32712 resize : function()
32715 var cs = this.el.getBox(true);
32717 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32718 Roo.log("no change in with or X");
32721 this.currentSize = cs;
32725 layout : function()
32728 this._resetLayout();
32729 //this._manageStamps();
32731 // don't animate first layout
32732 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32733 this.layoutItems( isInstant );
32735 // flag for initalized
32736 this._isLayoutInited = true;
32739 layoutItems : function( isInstant )
32741 //var items = this._getItemsForLayout( this.items );
32742 // original code supports filtering layout items.. we just ignore it..
32744 this._layoutItems( this.bricks , isInstant );
32746 this._postLayout();
32748 _layoutItems : function ( items , isInstant)
32750 //this.fireEvent( 'layout', this, items );
32753 if ( !items || !items.elements.length ) {
32754 // no items, emit event with empty array
32759 items.each(function(item) {
32760 Roo.log("layout item");
32762 // get x/y object from method
32763 var position = this._getItemLayoutPosition( item );
32765 position.item = item;
32766 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32767 queue.push( position );
32770 this._processLayoutQueue( queue );
32772 /** Sets position of item in DOM
32773 * @param {Element} item
32774 * @param {Number} x - horizontal position
32775 * @param {Number} y - vertical position
32776 * @param {Boolean} isInstant - disables transitions
32778 _processLayoutQueue : function( queue )
32780 for ( var i=0, len = queue.length; i < len; i++ ) {
32781 var obj = queue[i];
32782 obj.item.position('absolute');
32783 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32789 * Any logic you want to do after each layout,
32790 * i.e. size the container
32792 _postLayout : function()
32794 this.resizeContainer();
32797 resizeContainer : function()
32799 if ( !this.isResizingContainer ) {
32802 var size = this._getContainerSize();
32804 this.el.setSize(size.width,size.height);
32805 this.boxesEl.setSize(size.width,size.height);
32811 _resetLayout : function()
32813 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32814 this.colWidth = this.el.getWidth();
32815 //this.gutter = this.el.getWidth();
32817 this.measureColumns();
32823 this.colYs.push( 0 );
32829 measureColumns : function()
32831 this.getContainerWidth();
32832 // if columnWidth is 0, default to outerWidth of first item
32833 if ( !this.columnWidth ) {
32834 var firstItem = this.bricks.first();
32835 Roo.log(firstItem);
32836 this.columnWidth = this.containerWidth;
32837 if (firstItem && firstItem.attr('originalwidth') ) {
32838 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32840 // columnWidth fall back to item of first element
32841 Roo.log("set column width?");
32842 this.initialColumnWidth = this.columnWidth ;
32844 // if first elem has no width, default to size of container
32849 if (this.initialColumnWidth) {
32850 this.columnWidth = this.initialColumnWidth;
32855 // column width is fixed at the top - however if container width get's smaller we should
32858 // this bit calcs how man columns..
32860 var columnWidth = this.columnWidth += this.gutter;
32862 // calculate columns
32863 var containerWidth = this.containerWidth + this.gutter;
32865 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32866 // fix rounding errors, typically with gutters
32867 var excess = columnWidth - containerWidth % columnWidth;
32870 // if overshoot is less than a pixel, round up, otherwise floor it
32871 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32872 cols = Math[ mathMethod ]( cols );
32873 this.cols = Math.max( cols, 1 );
32874 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32876 // padding positioning..
32877 var totalColWidth = this.cols * this.columnWidth;
32878 var padavail = this.containerWidth - totalColWidth;
32879 // so for 2 columns - we need 3 'pads'
32881 var padNeeded = (1+this.cols) * this.padWidth;
32883 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32885 this.columnWidth += padExtra
32886 //this.padWidth = Math.floor(padavail / ( this.cols));
32888 // adjust colum width so that padding is fixed??
32890 // we have 3 columns ... total = width * 3
32891 // we have X left over... that should be used by
32893 //if (this.expandC) {
32901 getContainerWidth : function()
32903 /* // container is parent if fit width
32904 var container = this.isFitWidth ? this.element.parentNode : this.element;
32905 // check that this.size and size are there
32906 // IE8 triggers resize on body size change, so they might not be
32908 var size = getSize( container ); //FIXME
32909 this.containerWidth = size && size.innerWidth; //FIXME
32912 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32916 _getItemLayoutPosition : function( item ) // what is item?
32918 // we resize the item to our columnWidth..
32920 item.setWidth(this.columnWidth);
32921 item.autoBoxAdjust = false;
32923 var sz = item.getSize();
32925 // how many columns does this brick span
32926 var remainder = this.containerWidth % this.columnWidth;
32928 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32929 // round if off by 1 pixel, otherwise use ceil
32930 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32931 colSpan = Math.min( colSpan, this.cols );
32933 // normally this should be '1' as we dont' currently allow multi width columns..
32935 var colGroup = this._getColGroup( colSpan );
32936 // get the minimum Y value from the columns
32937 var minimumY = Math.min.apply( Math, colGroup );
32938 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32940 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32942 // position the brick
32944 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32945 y: this.currentSize.y + minimumY + this.padHeight
32949 // apply setHeight to necessary columns
32950 var setHeight = minimumY + sz.height + this.padHeight;
32951 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32953 var setSpan = this.cols + 1 - colGroup.length;
32954 for ( var i = 0; i < setSpan; i++ ) {
32955 this.colYs[ shortColIndex + i ] = setHeight ;
32962 * @param {Number} colSpan - number of columns the element spans
32963 * @returns {Array} colGroup
32965 _getColGroup : function( colSpan )
32967 if ( colSpan < 2 ) {
32968 // if brick spans only one column, use all the column Ys
32973 // how many different places could this brick fit horizontally
32974 var groupCount = this.cols + 1 - colSpan;
32975 // for each group potential horizontal position
32976 for ( var i = 0; i < groupCount; i++ ) {
32977 // make an array of colY values for that one group
32978 var groupColYs = this.colYs.slice( i, i + colSpan );
32979 // and get the max value of the array
32980 colGroup[i] = Math.max.apply( Math, groupColYs );
32985 _manageStamp : function( stamp )
32987 var stampSize = stamp.getSize();
32988 var offset = stamp.getBox();
32989 // get the columns that this stamp affects
32990 var firstX = this.isOriginLeft ? offset.x : offset.right;
32991 var lastX = firstX + stampSize.width;
32992 var firstCol = Math.floor( firstX / this.columnWidth );
32993 firstCol = Math.max( 0, firstCol );
32995 var lastCol = Math.floor( lastX / this.columnWidth );
32996 // lastCol should not go over if multiple of columnWidth #425
32997 lastCol -= lastX % this.columnWidth ? 0 : 1;
32998 lastCol = Math.min( this.cols - 1, lastCol );
33000 // set colYs to bottom of the stamp
33001 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33004 for ( var i = firstCol; i <= lastCol; i++ ) {
33005 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33010 _getContainerSize : function()
33012 this.maxY = Math.max.apply( Math, this.colYs );
33017 if ( this.isFitWidth ) {
33018 size.width = this._getContainerFitWidth();
33024 _getContainerFitWidth : function()
33026 var unusedCols = 0;
33027 // count unused columns
33030 if ( this.colYs[i] !== 0 ) {
33035 // fit container to columns that have been used
33036 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33039 needsResizeLayout : function()
33041 var previousWidth = this.containerWidth;
33042 this.getContainerWidth();
33043 return previousWidth !== this.containerWidth;
33058 * @class Roo.bootstrap.MasonryBrick
33059 * @extends Roo.bootstrap.Component
33060 * Bootstrap MasonryBrick class
33063 * Create a new MasonryBrick
33064 * @param {Object} config The config object
33067 Roo.bootstrap.MasonryBrick = function(config){
33069 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33071 Roo.bootstrap.MasonryBrick.register(this);
33077 * When a MasonryBrick is clcik
33078 * @param {Roo.bootstrap.MasonryBrick} this
33079 * @param {Roo.EventObject} e
33085 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
33088 * @cfg {String} title
33092 * @cfg {String} html
33096 * @cfg {String} bgimage
33100 * @cfg {String} videourl
33104 * @cfg {String} cls
33108 * @cfg {String} href
33112 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33117 * @cfg {String} placetitle (center|bottom)
33122 * @cfg {Boolean} isFitContainer defalut true
33124 isFitContainer : true,
33127 * @cfg {Boolean} preventDefault defalut false
33129 preventDefault : false,
33132 * @cfg {Boolean} inverse defalut false
33134 maskInverse : false,
33136 getAutoCreate : function()
33138 if(!this.isFitContainer){
33139 return this.getSplitAutoCreate();
33142 var cls = 'masonry-brick masonry-brick-full';
33144 if(this.href.length){
33145 cls += ' masonry-brick-link';
33148 if(this.bgimage.length){
33149 cls += ' masonry-brick-image';
33152 if(this.maskInverse){
33153 cls += ' mask-inverse';
33156 if(!this.html.length && !this.maskInverse && !this.videourl.length){
33157 cls += ' enable-mask';
33161 cls += ' masonry-' + this.size + '-brick';
33164 if(this.placetitle.length){
33166 switch (this.placetitle) {
33168 cls += ' masonry-center-title';
33171 cls += ' masonry-bottom-title';
33178 if(!this.html.length && !this.bgimage.length){
33179 cls += ' masonry-center-title';
33182 if(!this.html.length && this.bgimage.length){
33183 cls += ' masonry-bottom-title';
33188 cls += ' ' + this.cls;
33192 tag: (this.href.length) ? 'a' : 'div',
33197 cls: 'masonry-brick-mask'
33201 cls: 'masonry-brick-paragraph',
33207 if(this.href.length){
33208 cfg.href = this.href;
33211 var cn = cfg.cn[1].cn;
33213 if(this.title.length){
33216 cls: 'masonry-brick-title',
33221 if(this.html.length){
33224 cls: 'masonry-brick-text',
33229 if (!this.title.length && !this.html.length) {
33230 cfg.cn[1].cls += ' hide';
33233 if(this.bgimage.length){
33236 cls: 'masonry-brick-image-view',
33241 if(this.videourl.length){
33242 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33243 // youtube support only?
33246 cls: 'masonry-brick-image-view',
33249 allowfullscreen : true
33257 getSplitAutoCreate : function()
33259 var cls = 'masonry-brick masonry-brick-split';
33261 if(this.href.length){
33262 cls += ' masonry-brick-link';
33265 if(this.bgimage.length){
33266 cls += ' masonry-brick-image';
33270 cls += ' masonry-' + this.size + '-brick';
33273 switch (this.placetitle) {
33275 cls += ' masonry-center-title';
33278 cls += ' masonry-bottom-title';
33281 if(!this.bgimage.length){
33282 cls += ' masonry-center-title';
33285 if(this.bgimage.length){
33286 cls += ' masonry-bottom-title';
33292 cls += ' ' + this.cls;
33296 tag: (this.href.length) ? 'a' : 'div',
33301 cls: 'masonry-brick-split-head',
33305 cls: 'masonry-brick-paragraph',
33312 cls: 'masonry-brick-split-body',
33318 if(this.href.length){
33319 cfg.href = this.href;
33322 if(this.title.length){
33323 cfg.cn[0].cn[0].cn.push({
33325 cls: 'masonry-brick-title',
33330 if(this.html.length){
33331 cfg.cn[1].cn.push({
33333 cls: 'masonry-brick-text',
33338 if(this.bgimage.length){
33339 cfg.cn[0].cn.push({
33341 cls: 'masonry-brick-image-view',
33346 if(this.videourl.length){
33347 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33348 // youtube support only?
33349 cfg.cn[0].cn.cn.push({
33351 cls: 'masonry-brick-image-view',
33354 allowfullscreen : true
33361 initEvents: function()
33363 switch (this.size) {
33396 this.el.on('touchstart', this.onTouchStart, this);
33397 this.el.on('touchmove', this.onTouchMove, this);
33398 this.el.on('touchend', this.onTouchEnd, this);
33399 this.el.on('contextmenu', this.onContextMenu, this);
33401 this.el.on('mouseenter' ,this.enter, this);
33402 this.el.on('mouseleave', this.leave, this);
33403 this.el.on('click', this.onClick, this);
33406 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33407 this.parent().bricks.push(this);
33412 onClick: function(e, el)
33414 var time = this.endTimer - this.startTimer;
33415 // Roo.log(e.preventDefault());
33418 e.preventDefault();
33423 if(!this.preventDefault){
33427 e.preventDefault();
33429 if (this.activeClass != '') {
33430 this.selectBrick();
33433 this.fireEvent('click', this, e);
33436 enter: function(e, el)
33438 e.preventDefault();
33440 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33444 if(this.bgimage.length && this.html.length){
33445 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33449 leave: function(e, el)
33451 e.preventDefault();
33453 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33457 if(this.bgimage.length && this.html.length){
33458 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33462 onTouchStart: function(e, el)
33464 // e.preventDefault();
33466 this.touchmoved = false;
33468 if(!this.isFitContainer){
33472 if(!this.bgimage.length || !this.html.length){
33476 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33478 this.timer = new Date().getTime();
33482 onTouchMove: function(e, el)
33484 this.touchmoved = true;
33487 onContextMenu : function(e,el)
33489 e.preventDefault();
33490 e.stopPropagation();
33494 onTouchEnd: function(e, el)
33496 // e.preventDefault();
33498 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33505 if(!this.bgimage.length || !this.html.length){
33507 if(this.href.length){
33508 window.location.href = this.href;
33514 if(!this.isFitContainer){
33518 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33520 window.location.href = this.href;
33523 //selection on single brick only
33524 selectBrick : function() {
33526 if (!this.parentId) {
33530 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33531 var index = m.selectedBrick.indexOf(this.id);
33534 m.selectedBrick.splice(index,1);
33535 this.el.removeClass(this.activeClass);
33539 for(var i = 0; i < m.selectedBrick.length; i++) {
33540 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33541 b.el.removeClass(b.activeClass);
33544 m.selectedBrick = [];
33546 m.selectedBrick.push(this.id);
33547 this.el.addClass(this.activeClass);
33551 isSelected : function(){
33552 return this.el.hasClass(this.activeClass);
33557 Roo.apply(Roo.bootstrap.MasonryBrick, {
33560 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33562 * register a Masonry Brick
33563 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33566 register : function(brick)
33568 //this.groups[brick.id] = brick;
33569 this.groups.add(brick.id, brick);
33572 * fetch a masonry brick based on the masonry brick ID
33573 * @param {string} the masonry brick to add
33574 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33577 get: function(brick_id)
33579 // if (typeof(this.groups[brick_id]) == 'undefined') {
33582 // return this.groups[brick_id] ;
33584 if(this.groups.key(brick_id)) {
33585 return this.groups.key(brick_id);
33603 * @class Roo.bootstrap.Brick
33604 * @extends Roo.bootstrap.Component
33605 * Bootstrap Brick class
33608 * Create a new Brick
33609 * @param {Object} config The config object
33612 Roo.bootstrap.Brick = function(config){
33613 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33619 * When a Brick is click
33620 * @param {Roo.bootstrap.Brick} this
33621 * @param {Roo.EventObject} e
33627 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33630 * @cfg {String} title
33634 * @cfg {String} html
33638 * @cfg {String} bgimage
33642 * @cfg {String} cls
33646 * @cfg {String} href
33650 * @cfg {String} video
33654 * @cfg {Boolean} square
33658 getAutoCreate : function()
33660 var cls = 'roo-brick';
33662 if(this.href.length){
33663 cls += ' roo-brick-link';
33666 if(this.bgimage.length){
33667 cls += ' roo-brick-image';
33670 if(!this.html.length && !this.bgimage.length){
33671 cls += ' roo-brick-center-title';
33674 if(!this.html.length && this.bgimage.length){
33675 cls += ' roo-brick-bottom-title';
33679 cls += ' ' + this.cls;
33683 tag: (this.href.length) ? 'a' : 'div',
33688 cls: 'roo-brick-paragraph',
33694 if(this.href.length){
33695 cfg.href = this.href;
33698 var cn = cfg.cn[0].cn;
33700 if(this.title.length){
33703 cls: 'roo-brick-title',
33708 if(this.html.length){
33711 cls: 'roo-brick-text',
33718 if(this.bgimage.length){
33721 cls: 'roo-brick-image-view',
33729 initEvents: function()
33731 if(this.title.length || this.html.length){
33732 this.el.on('mouseenter' ,this.enter, this);
33733 this.el.on('mouseleave', this.leave, this);
33736 Roo.EventManager.onWindowResize(this.resize, this);
33738 if(this.bgimage.length){
33739 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33740 this.imageEl.on('load', this.onImageLoad, this);
33747 onImageLoad : function()
33752 resize : function()
33754 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33756 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33758 if(this.bgimage.length){
33759 var image = this.el.select('.roo-brick-image-view', true).first();
33761 image.setWidth(paragraph.getWidth());
33764 image.setHeight(paragraph.getWidth());
33767 this.el.setHeight(image.getHeight());
33768 paragraph.setHeight(image.getHeight());
33774 enter: function(e, el)
33776 e.preventDefault();
33778 if(this.bgimage.length){
33779 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33780 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33784 leave: function(e, el)
33786 e.preventDefault();
33788 if(this.bgimage.length){
33789 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33790 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33805 * @class Roo.bootstrap.NumberField
33806 * @extends Roo.bootstrap.Input
33807 * Bootstrap NumberField class
33813 * Create a new NumberField
33814 * @param {Object} config The config object
33817 Roo.bootstrap.NumberField = function(config){
33818 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33821 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33824 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33826 allowDecimals : true,
33828 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33830 decimalSeparator : ".",
33832 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33834 decimalPrecision : 2,
33836 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33838 allowNegative : true,
33841 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33845 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33847 minValue : Number.NEGATIVE_INFINITY,
33849 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33851 maxValue : Number.MAX_VALUE,
33853 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33855 minText : "The minimum value for this field is {0}",
33857 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33859 maxText : "The maximum value for this field is {0}",
33861 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33862 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33864 nanText : "{0} is not a valid number",
33866 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33868 thousandsDelimiter : false,
33870 * @cfg {String} valueAlign alignment of value
33872 valueAlign : "left",
33874 getAutoCreate : function()
33876 var hiddenInput = {
33880 cls: 'hidden-number-input'
33884 hiddenInput.name = this.name;
33889 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33891 this.name = hiddenInput.name;
33893 if(cfg.cn.length > 0) {
33894 cfg.cn.push(hiddenInput);
33901 initEvents : function()
33903 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33905 var allowed = "0123456789";
33907 if(this.allowDecimals){
33908 allowed += this.decimalSeparator;
33911 if(this.allowNegative){
33915 if(this.thousandsDelimiter) {
33919 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33921 var keyPress = function(e){
33923 var k = e.getKey();
33925 var c = e.getCharCode();
33928 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33929 allowed.indexOf(String.fromCharCode(c)) === -1
33935 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33939 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33944 this.el.on("keypress", keyPress, this);
33947 validateValue : function(value)
33950 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33954 var num = this.parseValue(value);
33957 this.markInvalid(String.format(this.nanText, value));
33961 if(num < this.minValue){
33962 this.markInvalid(String.format(this.minText, this.minValue));
33966 if(num > this.maxValue){
33967 this.markInvalid(String.format(this.maxText, this.maxValue));
33974 getValue : function()
33976 var v = this.hiddenEl().getValue();
33978 return this.fixPrecision(this.parseValue(v));
33981 parseValue : function(value)
33983 if(this.thousandsDelimiter) {
33985 r = new RegExp(",", "g");
33986 value = value.replace(r, "");
33989 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33990 return isNaN(value) ? '' : value;
33993 fixPrecision : function(value)
33995 if(this.thousandsDelimiter) {
33997 r = new RegExp(",", "g");
33998 value = value.replace(r, "");
34001 var nan = isNaN(value);
34003 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34004 return nan ? '' : value;
34006 return parseFloat(value).toFixed(this.decimalPrecision);
34009 setValue : function(v)
34011 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34017 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34019 this.inputEl().dom.value = (v == '') ? '' :
34020 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34022 if(!this.allowZero && v === '0') {
34023 this.hiddenEl().dom.value = '';
34024 this.inputEl().dom.value = '';
34031 decimalPrecisionFcn : function(v)
34033 return Math.floor(v);
34036 beforeBlur : function()
34038 var v = this.parseValue(this.getRawValue());
34040 if(v || v === 0 || v === ''){
34045 hiddenEl : function()
34047 return this.el.select('input.hidden-number-input',true).first();
34059 * @class Roo.bootstrap.DocumentSlider
34060 * @extends Roo.bootstrap.Component
34061 * Bootstrap DocumentSlider class
34064 * Create a new DocumentViewer
34065 * @param {Object} config The config object
34068 Roo.bootstrap.DocumentSlider = function(config){
34069 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34076 * Fire after initEvent
34077 * @param {Roo.bootstrap.DocumentSlider} this
34082 * Fire after update
34083 * @param {Roo.bootstrap.DocumentSlider} this
34089 * @param {Roo.bootstrap.DocumentSlider} this
34095 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
34101 getAutoCreate : function()
34105 cls : 'roo-document-slider',
34109 cls : 'roo-document-slider-header',
34113 cls : 'roo-document-slider-header-title'
34119 cls : 'roo-document-slider-body',
34123 cls : 'roo-document-slider-prev',
34127 cls : 'fa fa-chevron-left'
34133 cls : 'roo-document-slider-thumb',
34137 cls : 'roo-document-slider-image'
34143 cls : 'roo-document-slider-next',
34147 cls : 'fa fa-chevron-right'
34159 initEvents : function()
34161 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34162 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34164 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34165 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34167 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34168 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34170 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34171 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34173 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34174 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34176 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34177 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34179 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34180 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34182 this.thumbEl.on('click', this.onClick, this);
34184 this.prevIndicator.on('click', this.prev, this);
34186 this.nextIndicator.on('click', this.next, this);
34190 initial : function()
34192 if(this.files.length){
34193 this.indicator = 1;
34197 this.fireEvent('initial', this);
34200 update : function()
34202 this.imageEl.attr('src', this.files[this.indicator - 1]);
34204 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34206 this.prevIndicator.show();
34208 if(this.indicator == 1){
34209 this.prevIndicator.hide();
34212 this.nextIndicator.show();
34214 if(this.indicator == this.files.length){
34215 this.nextIndicator.hide();
34218 this.thumbEl.scrollTo('top');
34220 this.fireEvent('update', this);
34223 onClick : function(e)
34225 e.preventDefault();
34227 this.fireEvent('click', this);
34232 e.preventDefault();
34234 this.indicator = Math.max(1, this.indicator - 1);
34241 e.preventDefault();
34243 this.indicator = Math.min(this.files.length, this.indicator + 1);
34257 * @class Roo.bootstrap.RadioSet
34258 * @extends Roo.bootstrap.Input
34259 * Bootstrap RadioSet class
34260 * @cfg {String} indicatorpos (left|right) default left
34261 * @cfg {Boolean} inline (true|false) inline the element (default true)
34262 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34264 * Create a new RadioSet
34265 * @param {Object} config The config object
34268 Roo.bootstrap.RadioSet = function(config){
34270 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34274 Roo.bootstrap.RadioSet.register(this);
34279 * Fires when the element is checked or unchecked.
34280 * @param {Roo.bootstrap.RadioSet} this This radio
34281 * @param {Roo.bootstrap.Radio} item The checked item
34286 * Fires when the element is click.
34287 * @param {Roo.bootstrap.RadioSet} this This radio set
34288 * @param {Roo.bootstrap.Radio} item The checked item
34289 * @param {Roo.EventObject} e The event object
34296 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
34304 indicatorpos : 'left',
34306 getAutoCreate : function()
34310 cls : 'roo-radio-set-label',
34314 html : this.fieldLabel
34318 if (Roo.bootstrap.version == 3) {
34321 if(this.indicatorpos == 'left'){
34324 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34325 tooltip : 'This field is required'
34330 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34331 tooltip : 'This field is required'
34337 cls : 'roo-radio-set-items'
34340 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34342 if (align === 'left' && this.fieldLabel.length) {
34345 cls : "roo-radio-set-right",
34351 if(this.labelWidth > 12){
34352 label.style = "width: " + this.labelWidth + 'px';
34355 if(this.labelWidth < 13 && this.labelmd == 0){
34356 this.labelmd = this.labelWidth;
34359 if(this.labellg > 0){
34360 label.cls += ' col-lg-' + this.labellg;
34361 items.cls += ' col-lg-' + (12 - this.labellg);
34364 if(this.labelmd > 0){
34365 label.cls += ' col-md-' + this.labelmd;
34366 items.cls += ' col-md-' + (12 - this.labelmd);
34369 if(this.labelsm > 0){
34370 label.cls += ' col-sm-' + this.labelsm;
34371 items.cls += ' col-sm-' + (12 - this.labelsm);
34374 if(this.labelxs > 0){
34375 label.cls += ' col-xs-' + this.labelxs;
34376 items.cls += ' col-xs-' + (12 - this.labelxs);
34382 cls : 'roo-radio-set',
34386 cls : 'roo-radio-set-input',
34389 value : this.value ? this.value : ''
34396 if(this.weight.length){
34397 cfg.cls += ' roo-radio-' + this.weight;
34401 cfg.cls += ' roo-radio-set-inline';
34405 ['xs','sm','md','lg'].map(function(size){
34406 if (settings[size]) {
34407 cfg.cls += ' col-' + size + '-' + settings[size];
34415 initEvents : function()
34417 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34418 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34420 if(!this.fieldLabel.length){
34421 this.labelEl.hide();
34424 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34425 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34427 this.indicator = this.indicatorEl();
34429 if(this.indicator){
34430 this.indicator.addClass('invisible');
34433 this.originalValue = this.getValue();
34437 inputEl: function ()
34439 return this.el.select('.roo-radio-set-input', true).first();
34442 getChildContainer : function()
34444 return this.itemsEl;
34447 register : function(item)
34449 this.radioes.push(item);
34453 validate : function()
34455 if(this.getVisibilityEl().hasClass('hidden')){
34461 Roo.each(this.radioes, function(i){
34470 if(this.allowBlank) {
34474 if(this.disabled || valid){
34479 this.markInvalid();
34484 markValid : function()
34486 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34487 this.indicatorEl().removeClass('visible');
34488 this.indicatorEl().addClass('invisible');
34492 if (Roo.bootstrap.version == 3) {
34493 this.el.removeClass([this.invalidClass, this.validClass]);
34494 this.el.addClass(this.validClass);
34496 this.el.removeClass(['is-invalid','is-valid']);
34497 this.el.addClass(['is-valid']);
34499 this.fireEvent('valid', this);
34502 markInvalid : function(msg)
34504 if(this.allowBlank || this.disabled){
34508 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34509 this.indicatorEl().removeClass('invisible');
34510 this.indicatorEl().addClass('visible');
34512 if (Roo.bootstrap.version == 3) {
34513 this.el.removeClass([this.invalidClass, this.validClass]);
34514 this.el.addClass(this.invalidClass);
34516 this.el.removeClass(['is-invalid','is-valid']);
34517 this.el.addClass(['is-invalid']);
34520 this.fireEvent('invalid', this, msg);
34524 setValue : function(v, suppressEvent)
34526 if(this.value === v){
34533 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34536 Roo.each(this.radioes, function(i){
34538 i.el.removeClass('checked');
34541 Roo.each(this.radioes, function(i){
34543 if(i.value === v || i.value.toString() === v.toString()){
34545 i.el.addClass('checked');
34547 if(suppressEvent !== true){
34548 this.fireEvent('check', this, i);
34559 clearInvalid : function(){
34561 if(!this.el || this.preventMark){
34565 this.el.removeClass([this.invalidClass]);
34567 this.fireEvent('valid', this);
34572 Roo.apply(Roo.bootstrap.RadioSet, {
34576 register : function(set)
34578 this.groups[set.name] = set;
34581 get: function(name)
34583 if (typeof(this.groups[name]) == 'undefined') {
34587 return this.groups[name] ;
34593 * Ext JS Library 1.1.1
34594 * Copyright(c) 2006-2007, Ext JS, LLC.
34596 * Originally Released Under LGPL - original licence link has changed is not relivant.
34599 * <script type="text/javascript">
34604 * @class Roo.bootstrap.SplitBar
34605 * @extends Roo.util.Observable
34606 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34610 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34611 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34612 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34613 split.minSize = 100;
34614 split.maxSize = 600;
34615 split.animate = true;
34616 split.on('moved', splitterMoved);
34619 * Create a new SplitBar
34620 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34621 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34622 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34623 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34624 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34625 position of the SplitBar).
34627 Roo.bootstrap.SplitBar = function(cfg){
34632 // dragElement : elm
34633 // resizingElement: el,
34635 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34636 // placement : Roo.bootstrap.SplitBar.LEFT ,
34637 // existingProxy ???
34640 this.el = Roo.get(cfg.dragElement, true);
34641 this.el.dom.unselectable = "on";
34643 this.resizingEl = Roo.get(cfg.resizingElement, true);
34647 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34648 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34651 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34654 * The minimum size of the resizing element. (Defaults to 0)
34660 * The maximum size of the resizing element. (Defaults to 2000)
34663 this.maxSize = 2000;
34666 * Whether to animate the transition to the new size
34669 this.animate = false;
34672 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34675 this.useShim = false;
34680 if(!cfg.existingProxy){
34682 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34684 this.proxy = Roo.get(cfg.existingProxy).dom;
34687 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34690 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34693 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34696 this.dragSpecs = {};
34699 * @private The adapter to use to positon and resize elements
34701 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34702 this.adapter.init(this);
34704 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34706 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34707 this.el.addClass("roo-splitbar-h");
34710 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34711 this.el.addClass("roo-splitbar-v");
34717 * Fires when the splitter is moved (alias for {@link #event-moved})
34718 * @param {Roo.bootstrap.SplitBar} this
34719 * @param {Number} newSize the new width or height
34724 * Fires when the splitter is moved
34725 * @param {Roo.bootstrap.SplitBar} this
34726 * @param {Number} newSize the new width or height
34730 * @event beforeresize
34731 * Fires before the splitter is dragged
34732 * @param {Roo.bootstrap.SplitBar} this
34734 "beforeresize" : true,
34736 "beforeapply" : true
34739 Roo.util.Observable.call(this);
34742 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34743 onStartProxyDrag : function(x, y){
34744 this.fireEvent("beforeresize", this);
34746 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34748 o.enableDisplayMode("block");
34749 // all splitbars share the same overlay
34750 Roo.bootstrap.SplitBar.prototype.overlay = o;
34752 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34753 this.overlay.show();
34754 Roo.get(this.proxy).setDisplayed("block");
34755 var size = this.adapter.getElementSize(this);
34756 this.activeMinSize = this.getMinimumSize();;
34757 this.activeMaxSize = this.getMaximumSize();;
34758 var c1 = size - this.activeMinSize;
34759 var c2 = Math.max(this.activeMaxSize - size, 0);
34760 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34761 this.dd.resetConstraints();
34762 this.dd.setXConstraint(
34763 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34764 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34766 this.dd.setYConstraint(0, 0);
34768 this.dd.resetConstraints();
34769 this.dd.setXConstraint(0, 0);
34770 this.dd.setYConstraint(
34771 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34772 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34775 this.dragSpecs.startSize = size;
34776 this.dragSpecs.startPoint = [x, y];
34777 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34781 * @private Called after the drag operation by the DDProxy
34783 onEndProxyDrag : function(e){
34784 Roo.get(this.proxy).setDisplayed(false);
34785 var endPoint = Roo.lib.Event.getXY(e);
34787 this.overlay.hide();
34790 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34791 newSize = this.dragSpecs.startSize +
34792 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34793 endPoint[0] - this.dragSpecs.startPoint[0] :
34794 this.dragSpecs.startPoint[0] - endPoint[0]
34797 newSize = this.dragSpecs.startSize +
34798 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34799 endPoint[1] - this.dragSpecs.startPoint[1] :
34800 this.dragSpecs.startPoint[1] - endPoint[1]
34803 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34804 if(newSize != this.dragSpecs.startSize){
34805 if(this.fireEvent('beforeapply', this, newSize) !== false){
34806 this.adapter.setElementSize(this, newSize);
34807 this.fireEvent("moved", this, newSize);
34808 this.fireEvent("resize", this, newSize);
34814 * Get the adapter this SplitBar uses
34815 * @return The adapter object
34817 getAdapter : function(){
34818 return this.adapter;
34822 * Set the adapter this SplitBar uses
34823 * @param {Object} adapter A SplitBar adapter object
34825 setAdapter : function(adapter){
34826 this.adapter = adapter;
34827 this.adapter.init(this);
34831 * Gets the minimum size for the resizing element
34832 * @return {Number} The minimum size
34834 getMinimumSize : function(){
34835 return this.minSize;
34839 * Sets the minimum size for the resizing element
34840 * @param {Number} minSize The minimum size
34842 setMinimumSize : function(minSize){
34843 this.minSize = minSize;
34847 * Gets the maximum size for the resizing element
34848 * @return {Number} The maximum size
34850 getMaximumSize : function(){
34851 return this.maxSize;
34855 * Sets the maximum size for the resizing element
34856 * @param {Number} maxSize The maximum size
34858 setMaximumSize : function(maxSize){
34859 this.maxSize = maxSize;
34863 * Sets the initialize size for the resizing element
34864 * @param {Number} size The initial size
34866 setCurrentSize : function(size){
34867 var oldAnimate = this.animate;
34868 this.animate = false;
34869 this.adapter.setElementSize(this, size);
34870 this.animate = oldAnimate;
34874 * Destroy this splitbar.
34875 * @param {Boolean} removeEl True to remove the element
34877 destroy : function(removeEl){
34879 this.shim.remove();
34882 this.proxy.parentNode.removeChild(this.proxy);
34890 * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
34892 Roo.bootstrap.SplitBar.createProxy = function(dir){
34893 var proxy = new Roo.Element(document.createElement("div"));
34894 proxy.unselectable();
34895 var cls = 'roo-splitbar-proxy';
34896 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34897 document.body.appendChild(proxy.dom);
34902 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34903 * Default Adapter. It assumes the splitter and resizing element are not positioned
34904 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34906 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34909 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34910 // do nothing for now
34911 init : function(s){
34915 * Called before drag operations to get the current size of the resizing element.
34916 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34918 getElementSize : function(s){
34919 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34920 return s.resizingEl.getWidth();
34922 return s.resizingEl.getHeight();
34927 * Called after drag operations to set the size of the resizing element.
34928 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34929 * @param {Number} newSize The new size to set
34930 * @param {Function} onComplete A function to be invoked when resizing is complete
34932 setElementSize : function(s, newSize, onComplete){
34933 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34935 s.resizingEl.setWidth(newSize);
34937 onComplete(s, newSize);
34940 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34945 s.resizingEl.setHeight(newSize);
34947 onComplete(s, newSize);
34950 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34957 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34958 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34959 * Adapter that moves the splitter element to align with the resized sizing element.
34960 * Used with an absolute positioned SplitBar.
34961 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34962 * document.body, make sure you assign an id to the body element.
34964 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34965 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34966 this.container = Roo.get(container);
34969 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34970 init : function(s){
34971 this.basic.init(s);
34974 getElementSize : function(s){
34975 return this.basic.getElementSize(s);
34978 setElementSize : function(s, newSize, onComplete){
34979 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34982 moveSplitter : function(s){
34983 var yes = Roo.bootstrap.SplitBar;
34984 switch(s.placement){
34986 s.el.setX(s.resizingEl.getRight());
34989 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34992 s.el.setY(s.resizingEl.getBottom());
34995 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35002 * Orientation constant - Create a vertical SplitBar
35006 Roo.bootstrap.SplitBar.VERTICAL = 1;
35009 * Orientation constant - Create a horizontal SplitBar
35013 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35016 * Placement constant - The resizing element is to the left of the splitter element
35020 Roo.bootstrap.SplitBar.LEFT = 1;
35023 * Placement constant - The resizing element is to the right of the splitter element
35027 Roo.bootstrap.SplitBar.RIGHT = 2;
35030 * Placement constant - The resizing element is positioned above the splitter element
35034 Roo.bootstrap.SplitBar.TOP = 3;
35037 * Placement constant - The resizing element is positioned under splitter element
35041 Roo.bootstrap.SplitBar.BOTTOM = 4;
35042 Roo.namespace("Roo.bootstrap.layout");/*
35044 * Ext JS Library 1.1.1
35045 * Copyright(c) 2006-2007, Ext JS, LLC.
35047 * Originally Released Under LGPL - original licence link has changed is not relivant.
35050 * <script type="text/javascript">
35054 * @class Roo.bootstrap.layout.Manager
35055 * @extends Roo.bootstrap.Component
35056 * Base class for layout managers.
35058 Roo.bootstrap.layout.Manager = function(config)
35060 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35066 /** false to disable window resize monitoring @type Boolean */
35067 this.monitorWindowResize = true;
35072 * Fires when a layout is performed.
35073 * @param {Roo.LayoutManager} this
35077 * @event regionresized
35078 * Fires when the user resizes a region.
35079 * @param {Roo.LayoutRegion} region The resized region
35080 * @param {Number} newSize The new size (width for east/west, height for north/south)
35082 "regionresized" : true,
35084 * @event regioncollapsed
35085 * Fires when a region is collapsed.
35086 * @param {Roo.LayoutRegion} region The collapsed region
35088 "regioncollapsed" : true,
35090 * @event regionexpanded
35091 * Fires when a region is expanded.
35092 * @param {Roo.LayoutRegion} region The expanded region
35094 "regionexpanded" : true
35096 this.updating = false;
35099 this.el = Roo.get(config.el);
35105 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35110 monitorWindowResize : true,
35116 onRender : function(ct, position)
35119 this.el = Roo.get(ct);
35122 //this.fireEvent('render',this);
35126 initEvents: function()
35130 // ie scrollbar fix
35131 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35132 document.body.scroll = "no";
35133 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35134 this.el.position('relative');
35136 this.id = this.el.id;
35137 this.el.addClass("roo-layout-container");
35138 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35139 if(this.el.dom != document.body ) {
35140 this.el.on('resize', this.layout,this);
35141 this.el.on('show', this.layout,this);
35147 * Returns true if this layout is currently being updated
35148 * @return {Boolean}
35150 isUpdating : function(){
35151 return this.updating;
35155 * Suspend the LayoutManager from doing auto-layouts while
35156 * making multiple add or remove calls
35158 beginUpdate : function(){
35159 this.updating = true;
35163 * Restore auto-layouts and optionally disable the manager from performing a layout
35164 * @param {Boolean} noLayout true to disable a layout update
35166 endUpdate : function(noLayout){
35167 this.updating = false;
35173 layout: function(){
35177 onRegionResized : function(region, newSize){
35178 this.fireEvent("regionresized", region, newSize);
35182 onRegionCollapsed : function(region){
35183 this.fireEvent("regioncollapsed", region);
35186 onRegionExpanded : function(region){
35187 this.fireEvent("regionexpanded", region);
35191 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35192 * performs box-model adjustments.
35193 * @return {Object} The size as an object {width: (the width), height: (the height)}
35195 getViewSize : function()
35198 if(this.el.dom != document.body){
35199 size = this.el.getSize();
35201 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35203 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35204 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35209 * Returns the Element this layout is bound to.
35210 * @return {Roo.Element}
35212 getEl : function(){
35217 * Returns the specified region.
35218 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35219 * @return {Roo.LayoutRegion}
35221 getRegion : function(target){
35222 return this.regions[target.toLowerCase()];
35225 onWindowResize : function(){
35226 if(this.monitorWindowResize){
35233 * Ext JS Library 1.1.1
35234 * Copyright(c) 2006-2007, Ext JS, LLC.
35236 * Originally Released Under LGPL - original licence link has changed is not relivant.
35239 * <script type="text/javascript">
35242 * @class Roo.bootstrap.layout.Border
35243 * @extends Roo.bootstrap.layout.Manager
35244 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35245 * please see: examples/bootstrap/nested.html<br><br>
35247 <b>The container the layout is rendered into can be either the body element or any other element.
35248 If it is not the body element, the container needs to either be an absolute positioned element,
35249 or you will need to add "position:relative" to the css of the container. You will also need to specify
35250 the container size if it is not the body element.</b>
35253 * Create a new Border
35254 * @param {Object} config Configuration options
35256 Roo.bootstrap.layout.Border = function(config){
35257 config = config || {};
35258 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35262 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35263 if(config[region]){
35264 config[region].region = region;
35265 this.addRegion(config[region]);
35271 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
35273 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35275 parent : false, // this might point to a 'nest' or a ???
35278 * Creates and adds a new region if it doesn't already exist.
35279 * @param {String} target The target region key (north, south, east, west or center).
35280 * @param {Object} config The regions config object
35281 * @return {BorderLayoutRegion} The new region
35283 addRegion : function(config)
35285 if(!this.regions[config.region]){
35286 var r = this.factory(config);
35287 this.bindRegion(r);
35289 return this.regions[config.region];
35293 bindRegion : function(r){
35294 this.regions[r.config.region] = r;
35296 r.on("visibilitychange", this.layout, this);
35297 r.on("paneladded", this.layout, this);
35298 r.on("panelremoved", this.layout, this);
35299 r.on("invalidated", this.layout, this);
35300 r.on("resized", this.onRegionResized, this);
35301 r.on("collapsed", this.onRegionCollapsed, this);
35302 r.on("expanded", this.onRegionExpanded, this);
35306 * Performs a layout update.
35308 layout : function()
35310 if(this.updating) {
35314 // render all the rebions if they have not been done alreayd?
35315 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35316 if(this.regions[region] && !this.regions[region].bodyEl){
35317 this.regions[region].onRender(this.el)
35321 var size = this.getViewSize();
35322 var w = size.width;
35323 var h = size.height;
35328 //var x = 0, y = 0;
35330 var rs = this.regions;
35331 var north = rs["north"];
35332 var south = rs["south"];
35333 var west = rs["west"];
35334 var east = rs["east"];
35335 var center = rs["center"];
35336 //if(this.hideOnLayout){ // not supported anymore
35337 //c.el.setStyle("display", "none");
35339 if(north && north.isVisible()){
35340 var b = north.getBox();
35341 var m = north.getMargins();
35342 b.width = w - (m.left+m.right);
35345 centerY = b.height + b.y + m.bottom;
35346 centerH -= centerY;
35347 north.updateBox(this.safeBox(b));
35349 if(south && south.isVisible()){
35350 var b = south.getBox();
35351 var m = south.getMargins();
35352 b.width = w - (m.left+m.right);
35354 var totalHeight = (b.height + m.top + m.bottom);
35355 b.y = h - totalHeight + m.top;
35356 centerH -= totalHeight;
35357 south.updateBox(this.safeBox(b));
35359 if(west && west.isVisible()){
35360 var b = west.getBox();
35361 var m = west.getMargins();
35362 b.height = centerH - (m.top+m.bottom);
35364 b.y = centerY + m.top;
35365 var totalWidth = (b.width + m.left + m.right);
35366 centerX += totalWidth;
35367 centerW -= totalWidth;
35368 west.updateBox(this.safeBox(b));
35370 if(east && east.isVisible()){
35371 var b = east.getBox();
35372 var m = east.getMargins();
35373 b.height = centerH - (m.top+m.bottom);
35374 var totalWidth = (b.width + m.left + m.right);
35375 b.x = w - totalWidth + m.left;
35376 b.y = centerY + m.top;
35377 centerW -= totalWidth;
35378 east.updateBox(this.safeBox(b));
35381 var m = center.getMargins();
35383 x: centerX + m.left,
35384 y: centerY + m.top,
35385 width: centerW - (m.left+m.right),
35386 height: centerH - (m.top+m.bottom)
35388 //if(this.hideOnLayout){
35389 //center.el.setStyle("display", "block");
35391 center.updateBox(this.safeBox(centerBox));
35394 this.fireEvent("layout", this);
35398 safeBox : function(box){
35399 box.width = Math.max(0, box.width);
35400 box.height = Math.max(0, box.height);
35405 * Adds a ContentPanel (or subclass) to this layout.
35406 * @param {String} target The target region key (north, south, east, west or center).
35407 * @param {Roo.ContentPanel} panel The panel to add
35408 * @return {Roo.ContentPanel} The added panel
35410 add : function(target, panel){
35412 target = target.toLowerCase();
35413 return this.regions[target].add(panel);
35417 * Remove a ContentPanel (or subclass) to this layout.
35418 * @param {String} target The target region key (north, south, east, west or center).
35419 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35420 * @return {Roo.ContentPanel} The removed panel
35422 remove : function(target, panel){
35423 target = target.toLowerCase();
35424 return this.regions[target].remove(panel);
35428 * Searches all regions for a panel with the specified id
35429 * @param {String} panelId
35430 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35432 findPanel : function(panelId){
35433 var rs = this.regions;
35434 for(var target in rs){
35435 if(typeof rs[target] != "function"){
35436 var p = rs[target].getPanel(panelId);
35446 * Searches all regions for a panel with the specified id and activates (shows) it.
35447 * @param {String/ContentPanel} panelId The panels id or the panel itself
35448 * @return {Roo.ContentPanel} The shown panel or null
35450 showPanel : function(panelId) {
35451 var rs = this.regions;
35452 for(var target in rs){
35453 var r = rs[target];
35454 if(typeof r != "function"){
35455 if(r.hasPanel(panelId)){
35456 return r.showPanel(panelId);
35464 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35465 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35468 restoreState : function(provider){
35470 provider = Roo.state.Manager;
35472 var sm = new Roo.LayoutStateManager();
35473 sm.init(this, provider);
35479 * Adds a xtype elements to the layout.
35483 xtype : 'ContentPanel',
35490 xtype : 'NestedLayoutPanel',
35496 items : [ ... list of content panels or nested layout panels.. ]
35500 * @param {Object} cfg Xtype definition of item to add.
35502 addxtype : function(cfg)
35504 // basically accepts a pannel...
35505 // can accept a layout region..!?!?
35506 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35509 // theory? children can only be panels??
35511 //if (!cfg.xtype.match(/Panel$/)) {
35516 if (typeof(cfg.region) == 'undefined') {
35517 Roo.log("Failed to add Panel, region was not set");
35521 var region = cfg.region;
35527 xitems = cfg.items;
35532 if ( region == 'center') {
35533 Roo.log("Center: " + cfg.title);
35539 case 'Content': // ContentPanel (el, cfg)
35540 case 'Scroll': // ContentPanel (el, cfg)
35542 cfg.autoCreate = cfg.autoCreate || true;
35543 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35545 // var el = this.el.createChild();
35546 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35549 this.add(region, ret);
35553 case 'TreePanel': // our new panel!
35554 cfg.el = this.el.createChild();
35555 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35556 this.add(region, ret);
35561 // create a new Layout (which is a Border Layout...
35563 var clayout = cfg.layout;
35564 clayout.el = this.el.createChild();
35565 clayout.items = clayout.items || [];
35569 // replace this exitems with the clayout ones..
35570 xitems = clayout.items;
35572 // force background off if it's in center...
35573 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35574 cfg.background = false;
35576 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35579 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35580 //console.log('adding nested layout panel ' + cfg.toSource());
35581 this.add(region, ret);
35582 nb = {}; /// find first...
35587 // needs grid and region
35589 //var el = this.getRegion(region).el.createChild();
35591 *var el = this.el.createChild();
35592 // create the grid first...
35593 cfg.grid.container = el;
35594 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35597 if (region == 'center' && this.active ) {
35598 cfg.background = false;
35601 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35603 this.add(region, ret);
35605 if (cfg.background) {
35606 // render grid on panel activation (if panel background)
35607 ret.on('activate', function(gp) {
35608 if (!gp.grid.rendered) {
35609 // gp.grid.render(el);
35613 // cfg.grid.render(el);
35619 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35620 // it was the old xcomponent building that caused this before.
35621 // espeically if border is the top element in the tree.
35631 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35633 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35634 this.add(region, ret);
35638 throw "Can not add '" + cfg.xtype + "' to Border";
35644 this.beginUpdate();
35648 Roo.each(xitems, function(i) {
35649 region = nb && i.region ? i.region : false;
35651 var add = ret.addxtype(i);
35654 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35655 if (!i.background) {
35656 abn[region] = nb[region] ;
35663 // make the last non-background panel active..
35664 //if (nb) { Roo.log(abn); }
35667 for(var r in abn) {
35668 region = this.getRegion(r);
35670 // tried using nb[r], but it does not work..
35672 region.showPanel(abn[r]);
35683 factory : function(cfg)
35686 var validRegions = Roo.bootstrap.layout.Border.regions;
35688 var target = cfg.region;
35691 var r = Roo.bootstrap.layout;
35695 return new r.North(cfg);
35697 return new r.South(cfg);
35699 return new r.East(cfg);
35701 return new r.West(cfg);
35703 return new r.Center(cfg);
35705 throw 'Layout region "'+target+'" not supported.';
35712 * Ext JS Library 1.1.1
35713 * Copyright(c) 2006-2007, Ext JS, LLC.
35715 * Originally Released Under LGPL - original licence link has changed is not relivant.
35718 * <script type="text/javascript">
35722 * @class Roo.bootstrap.layout.Basic
35723 * @extends Roo.util.Observable
35724 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35725 * and does not have a titlebar, tabs or any other features. All it does is size and position
35726 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35727 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35728 * @cfg {string} region the region that it inhabits..
35729 * @cfg {bool} skipConfig skip config?
35733 Roo.bootstrap.layout.Basic = function(config){
35735 this.mgr = config.mgr;
35737 this.position = config.region;
35739 var skipConfig = config.skipConfig;
35743 * @scope Roo.BasicLayoutRegion
35747 * @event beforeremove
35748 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35749 * @param {Roo.LayoutRegion} this
35750 * @param {Roo.ContentPanel} panel The panel
35751 * @param {Object} e The cancel event object
35753 "beforeremove" : true,
35755 * @event invalidated
35756 * Fires when the layout for this region is changed.
35757 * @param {Roo.LayoutRegion} this
35759 "invalidated" : true,
35761 * @event visibilitychange
35762 * Fires when this region is shown or hidden
35763 * @param {Roo.LayoutRegion} this
35764 * @param {Boolean} visibility true or false
35766 "visibilitychange" : true,
35768 * @event paneladded
35769 * Fires when a panel is added.
35770 * @param {Roo.LayoutRegion} this
35771 * @param {Roo.ContentPanel} panel The panel
35773 "paneladded" : true,
35775 * @event panelremoved
35776 * Fires when a panel is removed.
35777 * @param {Roo.LayoutRegion} this
35778 * @param {Roo.ContentPanel} panel The panel
35780 "panelremoved" : true,
35782 * @event beforecollapse
35783 * Fires when this region before collapse.
35784 * @param {Roo.LayoutRegion} this
35786 "beforecollapse" : true,
35789 * Fires when this region is collapsed.
35790 * @param {Roo.LayoutRegion} this
35792 "collapsed" : true,
35795 * Fires when this region is expanded.
35796 * @param {Roo.LayoutRegion} this
35801 * Fires when this region is slid into view.
35802 * @param {Roo.LayoutRegion} this
35804 "slideshow" : true,
35807 * Fires when this region slides out of view.
35808 * @param {Roo.LayoutRegion} this
35810 "slidehide" : true,
35812 * @event panelactivated
35813 * Fires when a panel is activated.
35814 * @param {Roo.LayoutRegion} this
35815 * @param {Roo.ContentPanel} panel The activated panel
35817 "panelactivated" : true,
35820 * Fires when the user resizes this region.
35821 * @param {Roo.LayoutRegion} this
35822 * @param {Number} newSize The new size (width for east/west, height for north/south)
35826 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35827 this.panels = new Roo.util.MixedCollection();
35828 this.panels.getKey = this.getPanelId.createDelegate(this);
35830 this.activePanel = null;
35831 // ensure listeners are added...
35833 if (config.listeners || config.events) {
35834 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35835 listeners : config.listeners || {},
35836 events : config.events || {}
35840 if(skipConfig !== true){
35841 this.applyConfig(config);
35845 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35847 getPanelId : function(p){
35851 applyConfig : function(config){
35852 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35853 this.config = config;
35858 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35859 * the width, for horizontal (north, south) the height.
35860 * @param {Number} newSize The new width or height
35862 resizeTo : function(newSize){
35863 var el = this.el ? this.el :
35864 (this.activePanel ? this.activePanel.getEl() : null);
35866 switch(this.position){
35869 el.setWidth(newSize);
35870 this.fireEvent("resized", this, newSize);
35874 el.setHeight(newSize);
35875 this.fireEvent("resized", this, newSize);
35881 getBox : function(){
35882 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35885 getMargins : function(){
35886 return this.margins;
35889 updateBox : function(box){
35891 var el = this.activePanel.getEl();
35892 el.dom.style.left = box.x + "px";
35893 el.dom.style.top = box.y + "px";
35894 this.activePanel.setSize(box.width, box.height);
35898 * Returns the container element for this region.
35899 * @return {Roo.Element}
35901 getEl : function(){
35902 return this.activePanel;
35906 * Returns true if this region is currently visible.
35907 * @return {Boolean}
35909 isVisible : function(){
35910 return this.activePanel ? true : false;
35913 setActivePanel : function(panel){
35914 panel = this.getPanel(panel);
35915 if(this.activePanel && this.activePanel != panel){
35916 this.activePanel.setActiveState(false);
35917 this.activePanel.getEl().setLeftTop(-10000,-10000);
35919 this.activePanel = panel;
35920 panel.setActiveState(true);
35922 panel.setSize(this.box.width, this.box.height);
35924 this.fireEvent("panelactivated", this, panel);
35925 this.fireEvent("invalidated");
35929 * Show the specified panel.
35930 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35931 * @return {Roo.ContentPanel} The shown panel or null
35933 showPanel : function(panel){
35934 panel = this.getPanel(panel);
35936 this.setActivePanel(panel);
35942 * Get the active panel for this region.
35943 * @return {Roo.ContentPanel} The active panel or null
35945 getActivePanel : function(){
35946 return this.activePanel;
35950 * Add the passed ContentPanel(s)
35951 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35952 * @return {Roo.ContentPanel} The panel added (if only one was added)
35954 add : function(panel){
35955 if(arguments.length > 1){
35956 for(var i = 0, len = arguments.length; i < len; i++) {
35957 this.add(arguments[i]);
35961 if(this.hasPanel(panel)){
35962 this.showPanel(panel);
35965 var el = panel.getEl();
35966 if(el.dom.parentNode != this.mgr.el.dom){
35967 this.mgr.el.dom.appendChild(el.dom);
35969 if(panel.setRegion){
35970 panel.setRegion(this);
35972 this.panels.add(panel);
35973 el.setStyle("position", "absolute");
35974 if(!panel.background){
35975 this.setActivePanel(panel);
35976 if(this.config.initialSize && this.panels.getCount()==1){
35977 this.resizeTo(this.config.initialSize);
35980 this.fireEvent("paneladded", this, panel);
35985 * Returns true if the panel is in this region.
35986 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35987 * @return {Boolean}
35989 hasPanel : function(panel){
35990 if(typeof panel == "object"){ // must be panel obj
35991 panel = panel.getId();
35993 return this.getPanel(panel) ? true : false;
35997 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35998 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35999 * @param {Boolean} preservePanel Overrides the config preservePanel option
36000 * @return {Roo.ContentPanel} The panel that was removed
36002 remove : function(panel, preservePanel){
36003 panel = this.getPanel(panel);
36008 this.fireEvent("beforeremove", this, panel, e);
36009 if(e.cancel === true){
36012 var panelId = panel.getId();
36013 this.panels.removeKey(panelId);
36018 * Returns the panel specified or null if it's not in this region.
36019 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36020 * @return {Roo.ContentPanel}
36022 getPanel : function(id){
36023 if(typeof id == "object"){ // must be panel obj
36026 return this.panels.get(id);
36030 * Returns this regions position (north/south/east/west/center).
36033 getPosition: function(){
36034 return this.position;
36038 * Ext JS Library 1.1.1
36039 * Copyright(c) 2006-2007, Ext JS, LLC.
36041 * Originally Released Under LGPL - original licence link has changed is not relivant.
36044 * <script type="text/javascript">
36048 * @class Roo.bootstrap.layout.Region
36049 * @extends Roo.bootstrap.layout.Basic
36050 * This class represents a region in a layout manager.
36052 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36053 * @cfg {Object} cmargins Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
36054 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
36055 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
36056 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
36057 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
36058 * @cfg {String} title The title for the region (overrides panel titles)
36059 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
36060 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36061 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
36062 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36063 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
36064 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36065 * the space available, similar to FireFox 1.5 tabs (defaults to false)
36066 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
36067 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
36068 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
36070 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
36071 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
36072 * @cfg {Boolean} disableTabTips True to disable tab tooltips
36073 * @cfg {Number} width For East/West panels
36074 * @cfg {Number} height For North/South panels
36075 * @cfg {Boolean} split To show the splitter
36076 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
36078 * @cfg {string} cls Extra CSS classes to add to region
36080 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
36081 * @cfg {string} region the region that it inhabits..
36084 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
36085 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
36087 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
36088 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
36089 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
36091 Roo.bootstrap.layout.Region = function(config)
36093 this.applyConfig(config);
36095 var mgr = config.mgr;
36096 var pos = config.region;
36097 config.skipConfig = true;
36098 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36101 this.onRender(mgr.el);
36104 this.visible = true;
36105 this.collapsed = false;
36106 this.unrendered_panels = [];
36109 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36111 position: '', // set by wrapper (eg. north/south etc..)
36112 unrendered_panels : null, // unrendered panels.
36114 tabPosition : false,
36116 mgr: false, // points to 'Border'
36119 createBody : function(){
36120 /** This region's body element
36121 * @type Roo.Element */
36122 this.bodyEl = this.el.createChild({
36124 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36128 onRender: function(ctr, pos)
36130 var dh = Roo.DomHelper;
36131 /** This region's container element
36132 * @type Roo.Element */
36133 this.el = dh.append(ctr.dom, {
36135 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36137 /** This region's title element
36138 * @type Roo.Element */
36140 this.titleEl = dh.append(this.el.dom, {
36142 unselectable: "on",
36143 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36145 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
36146 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36150 this.titleEl.enableDisplayMode();
36151 /** This region's title text element
36152 * @type HTMLElement */
36153 this.titleTextEl = this.titleEl.dom.firstChild;
36154 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36156 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36157 this.closeBtn.enableDisplayMode();
36158 this.closeBtn.on("click", this.closeClicked, this);
36159 this.closeBtn.hide();
36161 this.createBody(this.config);
36162 if(this.config.hideWhenEmpty){
36164 this.on("paneladded", this.validateVisibility, this);
36165 this.on("panelremoved", this.validateVisibility, this);
36167 if(this.autoScroll){
36168 this.bodyEl.setStyle("overflow", "auto");
36170 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36172 //if(c.titlebar !== false){
36173 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36174 this.titleEl.hide();
36176 this.titleEl.show();
36177 if(this.config.title){
36178 this.titleTextEl.innerHTML = this.config.title;
36182 if(this.config.collapsed){
36183 this.collapse(true);
36185 if(this.config.hidden){
36189 if (this.unrendered_panels && this.unrendered_panels.length) {
36190 for (var i =0;i< this.unrendered_panels.length; i++) {
36191 this.add(this.unrendered_panels[i]);
36193 this.unrendered_panels = null;
36199 applyConfig : function(c)
36202 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36203 var dh = Roo.DomHelper;
36204 if(c.titlebar !== false){
36205 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36206 this.collapseBtn.on("click", this.collapse, this);
36207 this.collapseBtn.enableDisplayMode();
36209 if(c.showPin === true || this.showPin){
36210 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36211 this.stickBtn.enableDisplayMode();
36212 this.stickBtn.on("click", this.expand, this);
36213 this.stickBtn.hide();
36218 /** This region's collapsed element
36219 * @type Roo.Element */
36222 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36223 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36226 if(c.floatable !== false){
36227 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36228 this.collapsedEl.on("click", this.collapseClick, this);
36231 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36232 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36233 id: "message", unselectable: "on", style:{"float":"left"}});
36234 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36236 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36237 this.expandBtn.on("click", this.expand, this);
36241 if(this.collapseBtn){
36242 this.collapseBtn.setVisible(c.collapsible == true);
36245 this.cmargins = c.cmargins || this.cmargins ||
36246 (this.position == "west" || this.position == "east" ?
36247 {top: 0, left: 2, right:2, bottom: 0} :
36248 {top: 2, left: 0, right:0, bottom: 2});
36250 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36253 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36255 this.autoScroll = c.autoScroll || false;
36260 this.duration = c.duration || .30;
36261 this.slideDuration = c.slideDuration || .45;
36266 * Returns true if this region is currently visible.
36267 * @return {Boolean}
36269 isVisible : function(){
36270 return this.visible;
36274 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36275 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
36277 //setCollapsedTitle : function(title){
36278 // title = title || " ";
36279 // if(this.collapsedTitleTextEl){
36280 // this.collapsedTitleTextEl.innerHTML = title;
36284 getBox : function(){
36286 // if(!this.collapsed){
36287 b = this.el.getBox(false, true);
36289 // b = this.collapsedEl.getBox(false, true);
36294 getMargins : function(){
36295 return this.margins;
36296 //return this.collapsed ? this.cmargins : this.margins;
36299 highlight : function(){
36300 this.el.addClass("x-layout-panel-dragover");
36303 unhighlight : function(){
36304 this.el.removeClass("x-layout-panel-dragover");
36307 updateBox : function(box)
36309 if (!this.bodyEl) {
36310 return; // not rendered yet..
36314 if(!this.collapsed){
36315 this.el.dom.style.left = box.x + "px";
36316 this.el.dom.style.top = box.y + "px";
36317 this.updateBody(box.width, box.height);
36319 this.collapsedEl.dom.style.left = box.x + "px";
36320 this.collapsedEl.dom.style.top = box.y + "px";
36321 this.collapsedEl.setSize(box.width, box.height);
36324 this.tabs.autoSizeTabs();
36328 updateBody : function(w, h)
36331 this.el.setWidth(w);
36332 w -= this.el.getBorderWidth("rl");
36333 if(this.config.adjustments){
36334 w += this.config.adjustments[0];
36337 if(h !== null && h > 0){
36338 this.el.setHeight(h);
36339 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36340 h -= this.el.getBorderWidth("tb");
36341 if(this.config.adjustments){
36342 h += this.config.adjustments[1];
36344 this.bodyEl.setHeight(h);
36346 h = this.tabs.syncHeight(h);
36349 if(this.panelSize){
36350 w = w !== null ? w : this.panelSize.width;
36351 h = h !== null ? h : this.panelSize.height;
36353 if(this.activePanel){
36354 var el = this.activePanel.getEl();
36355 w = w !== null ? w : el.getWidth();
36356 h = h !== null ? h : el.getHeight();
36357 this.panelSize = {width: w, height: h};
36358 this.activePanel.setSize(w, h);
36360 if(Roo.isIE && this.tabs){
36361 this.tabs.el.repaint();
36366 * Returns the container element for this region.
36367 * @return {Roo.Element}
36369 getEl : function(){
36374 * Hides this region.
36377 //if(!this.collapsed){
36378 this.el.dom.style.left = "-2000px";
36381 // this.collapsedEl.dom.style.left = "-2000px";
36382 // this.collapsedEl.hide();
36384 this.visible = false;
36385 this.fireEvent("visibilitychange", this, false);
36389 * Shows this region if it was previously hidden.
36392 //if(!this.collapsed){
36395 // this.collapsedEl.show();
36397 this.visible = true;
36398 this.fireEvent("visibilitychange", this, true);
36401 closeClicked : function(){
36402 if(this.activePanel){
36403 this.remove(this.activePanel);
36407 collapseClick : function(e){
36409 e.stopPropagation();
36412 e.stopPropagation();
36418 * Collapses this region.
36419 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36422 collapse : function(skipAnim, skipCheck = false){
36423 if(this.collapsed) {
36427 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36429 this.collapsed = true;
36431 this.split.el.hide();
36433 if(this.config.animate && skipAnim !== true){
36434 this.fireEvent("invalidated", this);
36435 this.animateCollapse();
36437 this.el.setLocation(-20000,-20000);
36439 this.collapsedEl.show();
36440 this.fireEvent("collapsed", this);
36441 this.fireEvent("invalidated", this);
36447 animateCollapse : function(){
36452 * Expands this region if it was previously collapsed.
36453 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36454 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36457 expand : function(e, skipAnim){
36459 e.stopPropagation();
36461 if(!this.collapsed || this.el.hasActiveFx()) {
36465 this.afterSlideIn();
36468 this.collapsed = false;
36469 if(this.config.animate && skipAnim !== true){
36470 this.animateExpand();
36474 this.split.el.show();
36476 this.collapsedEl.setLocation(-2000,-2000);
36477 this.collapsedEl.hide();
36478 this.fireEvent("invalidated", this);
36479 this.fireEvent("expanded", this);
36483 animateExpand : function(){
36487 initTabs : function()
36489 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36491 var ts = new Roo.bootstrap.panel.Tabs({
36492 el: this.bodyEl.dom,
36494 tabPosition: this.tabPosition ? this.tabPosition : 'top',
36495 disableTooltips: this.config.disableTabTips,
36496 toolbar : this.config.toolbar
36499 if(this.config.hideTabs){
36500 ts.stripWrap.setDisplayed(false);
36503 ts.resizeTabs = this.config.resizeTabs === true;
36504 ts.minTabWidth = this.config.minTabWidth || 40;
36505 ts.maxTabWidth = this.config.maxTabWidth || 250;
36506 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36507 ts.monitorResize = false;
36508 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36509 ts.bodyEl.addClass('roo-layout-tabs-body');
36510 this.panels.each(this.initPanelAsTab, this);
36513 initPanelAsTab : function(panel){
36514 var ti = this.tabs.addTab(
36518 this.config.closeOnTab && panel.isClosable(),
36521 if(panel.tabTip !== undefined){
36522 ti.setTooltip(panel.tabTip);
36524 ti.on("activate", function(){
36525 this.setActivePanel(panel);
36528 if(this.config.closeOnTab){
36529 ti.on("beforeclose", function(t, e){
36531 this.remove(panel);
36535 panel.tabItem = ti;
36540 updatePanelTitle : function(panel, title)
36542 if(this.activePanel == panel){
36543 this.updateTitle(title);
36546 var ti = this.tabs.getTab(panel.getEl().id);
36548 if(panel.tabTip !== undefined){
36549 ti.setTooltip(panel.tabTip);
36554 updateTitle : function(title){
36555 if(this.titleTextEl && !this.config.title){
36556 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36560 setActivePanel : function(panel)
36562 panel = this.getPanel(panel);
36563 if(this.activePanel && this.activePanel != panel){
36564 if(this.activePanel.setActiveState(false) === false){
36568 this.activePanel = panel;
36569 panel.setActiveState(true);
36570 if(this.panelSize){
36571 panel.setSize(this.panelSize.width, this.panelSize.height);
36574 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36576 this.updateTitle(panel.getTitle());
36578 this.fireEvent("invalidated", this);
36580 this.fireEvent("panelactivated", this, panel);
36584 * Shows the specified panel.
36585 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36586 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36588 showPanel : function(panel)
36590 panel = this.getPanel(panel);
36593 var tab = this.tabs.getTab(panel.getEl().id);
36594 if(tab.isHidden()){
36595 this.tabs.unhideTab(tab.id);
36599 this.setActivePanel(panel);
36606 * Get the active panel for this region.
36607 * @return {Roo.ContentPanel} The active panel or null
36609 getActivePanel : function(){
36610 return this.activePanel;
36613 validateVisibility : function(){
36614 if(this.panels.getCount() < 1){
36615 this.updateTitle(" ");
36616 this.closeBtn.hide();
36619 if(!this.isVisible()){
36626 * Adds the passed ContentPanel(s) to this region.
36627 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36628 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36630 add : function(panel)
36632 if(arguments.length > 1){
36633 for(var i = 0, len = arguments.length; i < len; i++) {
36634 this.add(arguments[i]);
36639 // if we have not been rendered yet, then we can not really do much of this..
36640 if (!this.bodyEl) {
36641 this.unrendered_panels.push(panel);
36648 if(this.hasPanel(panel)){
36649 this.showPanel(panel);
36652 panel.setRegion(this);
36653 this.panels.add(panel);
36654 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36655 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36656 // and hide them... ???
36657 this.bodyEl.dom.appendChild(panel.getEl().dom);
36658 if(panel.background !== true){
36659 this.setActivePanel(panel);
36661 this.fireEvent("paneladded", this, panel);
36668 this.initPanelAsTab(panel);
36672 if(panel.background !== true){
36673 this.tabs.activate(panel.getEl().id);
36675 this.fireEvent("paneladded", this, panel);
36680 * Hides the tab for the specified panel.
36681 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36683 hidePanel : function(panel){
36684 if(this.tabs && (panel = this.getPanel(panel))){
36685 this.tabs.hideTab(panel.getEl().id);
36690 * Unhides the tab for a previously hidden panel.
36691 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36693 unhidePanel : function(panel){
36694 if(this.tabs && (panel = this.getPanel(panel))){
36695 this.tabs.unhideTab(panel.getEl().id);
36699 clearPanels : function(){
36700 while(this.panels.getCount() > 0){
36701 this.remove(this.panels.first());
36706 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36707 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36708 * @param {Boolean} preservePanel Overrides the config preservePanel option
36709 * @return {Roo.ContentPanel} The panel that was removed
36711 remove : function(panel, preservePanel)
36713 panel = this.getPanel(panel);
36718 this.fireEvent("beforeremove", this, panel, e);
36719 if(e.cancel === true){
36722 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36723 var panelId = panel.getId();
36724 this.panels.removeKey(panelId);
36726 document.body.appendChild(panel.getEl().dom);
36729 this.tabs.removeTab(panel.getEl().id);
36730 }else if (!preservePanel){
36731 this.bodyEl.dom.removeChild(panel.getEl().dom);
36733 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36734 var p = this.panels.first();
36735 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36736 tempEl.appendChild(p.getEl().dom);
36737 this.bodyEl.update("");
36738 this.bodyEl.dom.appendChild(p.getEl().dom);
36740 this.updateTitle(p.getTitle());
36742 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36743 this.setActivePanel(p);
36745 panel.setRegion(null);
36746 if(this.activePanel == panel){
36747 this.activePanel = null;
36749 if(this.config.autoDestroy !== false && preservePanel !== true){
36750 try{panel.destroy();}catch(e){}
36752 this.fireEvent("panelremoved", this, panel);
36757 * Returns the TabPanel component used by this region
36758 * @return {Roo.TabPanel}
36760 getTabs : function(){
36764 createTool : function(parentEl, className){
36765 var btn = Roo.DomHelper.append(parentEl, {
36767 cls: "x-layout-tools-button",
36770 cls: "roo-layout-tools-button-inner " + className,
36774 btn.addClassOnOver("roo-layout-tools-button-over");
36779 * Ext JS Library 1.1.1
36780 * Copyright(c) 2006-2007, Ext JS, LLC.
36782 * Originally Released Under LGPL - original licence link has changed is not relivant.
36785 * <script type="text/javascript">
36791 * @class Roo.SplitLayoutRegion
36792 * @extends Roo.LayoutRegion
36793 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36795 Roo.bootstrap.layout.Split = function(config){
36796 this.cursor = config.cursor;
36797 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36800 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36802 splitTip : "Drag to resize.",
36803 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36804 useSplitTips : false,
36806 applyConfig : function(config){
36807 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36810 onRender : function(ctr,pos) {
36812 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36813 if(!this.config.split){
36818 var splitEl = Roo.DomHelper.append(ctr.dom, {
36820 id: this.el.id + "-split",
36821 cls: "roo-layout-split roo-layout-split-"+this.position,
36824 /** The SplitBar for this region
36825 * @type Roo.SplitBar */
36826 // does not exist yet...
36827 Roo.log([this.position, this.orientation]);
36829 this.split = new Roo.bootstrap.SplitBar({
36830 dragElement : splitEl,
36831 resizingElement: this.el,
36832 orientation : this.orientation
36835 this.split.on("moved", this.onSplitMove, this);
36836 this.split.useShim = this.config.useShim === true;
36837 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36838 if(this.useSplitTips){
36839 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36841 //if(config.collapsible){
36842 // this.split.el.on("dblclick", this.collapse, this);
36845 if(typeof this.config.minSize != "undefined"){
36846 this.split.minSize = this.config.minSize;
36848 if(typeof this.config.maxSize != "undefined"){
36849 this.split.maxSize = this.config.maxSize;
36851 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36852 this.hideSplitter();
36857 getHMaxSize : function(){
36858 var cmax = this.config.maxSize || 10000;
36859 var center = this.mgr.getRegion("center");
36860 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36863 getVMaxSize : function(){
36864 var cmax = this.config.maxSize || 10000;
36865 var center = this.mgr.getRegion("center");
36866 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36869 onSplitMove : function(split, newSize){
36870 this.fireEvent("resized", this, newSize);
36874 * Returns the {@link Roo.SplitBar} for this region.
36875 * @return {Roo.SplitBar}
36877 getSplitBar : function(){
36882 this.hideSplitter();
36883 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36886 hideSplitter : function(){
36888 this.split.el.setLocation(-2000,-2000);
36889 this.split.el.hide();
36895 this.split.el.show();
36897 Roo.bootstrap.layout.Split.superclass.show.call(this);
36900 beforeSlide: function(){
36901 if(Roo.isGecko){// firefox overflow auto bug workaround
36902 this.bodyEl.clip();
36904 this.tabs.bodyEl.clip();
36906 if(this.activePanel){
36907 this.activePanel.getEl().clip();
36909 if(this.activePanel.beforeSlide){
36910 this.activePanel.beforeSlide();
36916 afterSlide : function(){
36917 if(Roo.isGecko){// firefox overflow auto bug workaround
36918 this.bodyEl.unclip();
36920 this.tabs.bodyEl.unclip();
36922 if(this.activePanel){
36923 this.activePanel.getEl().unclip();
36924 if(this.activePanel.afterSlide){
36925 this.activePanel.afterSlide();
36931 initAutoHide : function(){
36932 if(this.autoHide !== false){
36933 if(!this.autoHideHd){
36934 var st = new Roo.util.DelayedTask(this.slideIn, this);
36935 this.autoHideHd = {
36936 "mouseout": function(e){
36937 if(!e.within(this.el, true)){
36941 "mouseover" : function(e){
36947 this.el.on(this.autoHideHd);
36951 clearAutoHide : function(){
36952 if(this.autoHide !== false){
36953 this.el.un("mouseout", this.autoHideHd.mouseout);
36954 this.el.un("mouseover", this.autoHideHd.mouseover);
36958 clearMonitor : function(){
36959 Roo.get(document).un("click", this.slideInIf, this);
36962 // these names are backwards but not changed for compat
36963 slideOut : function(){
36964 if(this.isSlid || this.el.hasActiveFx()){
36967 this.isSlid = true;
36968 if(this.collapseBtn){
36969 this.collapseBtn.hide();
36971 this.closeBtnState = this.closeBtn.getStyle('display');
36972 this.closeBtn.hide();
36974 this.stickBtn.show();
36977 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36978 this.beforeSlide();
36979 this.el.setStyle("z-index", 10001);
36980 this.el.slideIn(this.getSlideAnchor(), {
36981 callback: function(){
36983 this.initAutoHide();
36984 Roo.get(document).on("click", this.slideInIf, this);
36985 this.fireEvent("slideshow", this);
36992 afterSlideIn : function(){
36993 this.clearAutoHide();
36994 this.isSlid = false;
36995 this.clearMonitor();
36996 this.el.setStyle("z-index", "");
36997 if(this.collapseBtn){
36998 this.collapseBtn.show();
37000 this.closeBtn.setStyle('display', this.closeBtnState);
37002 this.stickBtn.hide();
37004 this.fireEvent("slidehide", this);
37007 slideIn : function(cb){
37008 if(!this.isSlid || this.el.hasActiveFx()){
37012 this.isSlid = false;
37013 this.beforeSlide();
37014 this.el.slideOut(this.getSlideAnchor(), {
37015 callback: function(){
37016 this.el.setLeftTop(-10000, -10000);
37018 this.afterSlideIn();
37026 slideInIf : function(e){
37027 if(!e.within(this.el)){
37032 animateCollapse : function(){
37033 this.beforeSlide();
37034 this.el.setStyle("z-index", 20000);
37035 var anchor = this.getSlideAnchor();
37036 this.el.slideOut(anchor, {
37037 callback : function(){
37038 this.el.setStyle("z-index", "");
37039 this.collapsedEl.slideIn(anchor, {duration:.3});
37041 this.el.setLocation(-10000,-10000);
37043 this.fireEvent("collapsed", this);
37050 animateExpand : function(){
37051 this.beforeSlide();
37052 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37053 this.el.setStyle("z-index", 20000);
37054 this.collapsedEl.hide({
37057 this.el.slideIn(this.getSlideAnchor(), {
37058 callback : function(){
37059 this.el.setStyle("z-index", "");
37062 this.split.el.show();
37064 this.fireEvent("invalidated", this);
37065 this.fireEvent("expanded", this);
37093 getAnchor : function(){
37094 return this.anchors[this.position];
37097 getCollapseAnchor : function(){
37098 return this.canchors[this.position];
37101 getSlideAnchor : function(){
37102 return this.sanchors[this.position];
37105 getAlignAdj : function(){
37106 var cm = this.cmargins;
37107 switch(this.position){
37123 getExpandAdj : function(){
37124 var c = this.collapsedEl, cm = this.cmargins;
37125 switch(this.position){
37127 return [-(cm.right+c.getWidth()+cm.left), 0];
37130 return [cm.right+c.getWidth()+cm.left, 0];
37133 return [0, -(cm.top+cm.bottom+c.getHeight())];
37136 return [0, cm.top+cm.bottom+c.getHeight()];
37142 * Ext JS Library 1.1.1
37143 * Copyright(c) 2006-2007, Ext JS, LLC.
37145 * Originally Released Under LGPL - original licence link has changed is not relivant.
37148 * <script type="text/javascript">
37151 * These classes are private internal classes
37153 Roo.bootstrap.layout.Center = function(config){
37154 config.region = "center";
37155 Roo.bootstrap.layout.Region.call(this, config);
37156 this.visible = true;
37157 this.minWidth = config.minWidth || 20;
37158 this.minHeight = config.minHeight || 20;
37161 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37163 // center panel can't be hidden
37167 // center panel can't be hidden
37170 getMinWidth: function(){
37171 return this.minWidth;
37174 getMinHeight: function(){
37175 return this.minHeight;
37189 Roo.bootstrap.layout.North = function(config)
37191 config.region = 'north';
37192 config.cursor = 'n-resize';
37194 Roo.bootstrap.layout.Split.call(this, config);
37198 this.split.placement = Roo.bootstrap.SplitBar.TOP;
37199 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37200 this.split.el.addClass("roo-layout-split-v");
37202 var size = config.initialSize || config.height;
37203 if(typeof size != "undefined"){
37204 this.el.setHeight(size);
37207 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37209 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37213 getBox : function(){
37214 if(this.collapsed){
37215 return this.collapsedEl.getBox();
37217 var box = this.el.getBox();
37219 box.height += this.split.el.getHeight();
37224 updateBox : function(box){
37225 if(this.split && !this.collapsed){
37226 box.height -= this.split.el.getHeight();
37227 this.split.el.setLeft(box.x);
37228 this.split.el.setTop(box.y+box.height);
37229 this.split.el.setWidth(box.width);
37231 if(this.collapsed){
37232 this.updateBody(box.width, null);
37234 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37242 Roo.bootstrap.layout.South = function(config){
37243 config.region = 'south';
37244 config.cursor = 's-resize';
37245 Roo.bootstrap.layout.Split.call(this, config);
37247 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37248 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37249 this.split.el.addClass("roo-layout-split-v");
37251 var size = config.initialSize || config.height;
37252 if(typeof size != "undefined"){
37253 this.el.setHeight(size);
37257 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37258 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37259 getBox : function(){
37260 if(this.collapsed){
37261 return this.collapsedEl.getBox();
37263 var box = this.el.getBox();
37265 var sh = this.split.el.getHeight();
37272 updateBox : function(box){
37273 if(this.split && !this.collapsed){
37274 var sh = this.split.el.getHeight();
37277 this.split.el.setLeft(box.x);
37278 this.split.el.setTop(box.y-sh);
37279 this.split.el.setWidth(box.width);
37281 if(this.collapsed){
37282 this.updateBody(box.width, null);
37284 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37288 Roo.bootstrap.layout.East = function(config){
37289 config.region = "east";
37290 config.cursor = "e-resize";
37291 Roo.bootstrap.layout.Split.call(this, config);
37293 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37294 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37295 this.split.el.addClass("roo-layout-split-h");
37297 var size = config.initialSize || config.width;
37298 if(typeof size != "undefined"){
37299 this.el.setWidth(size);
37302 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37303 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37304 getBox : function(){
37305 if(this.collapsed){
37306 return this.collapsedEl.getBox();
37308 var box = this.el.getBox();
37310 var sw = this.split.el.getWidth();
37317 updateBox : function(box){
37318 if(this.split && !this.collapsed){
37319 var sw = this.split.el.getWidth();
37321 this.split.el.setLeft(box.x);
37322 this.split.el.setTop(box.y);
37323 this.split.el.setHeight(box.height);
37326 if(this.collapsed){
37327 this.updateBody(null, box.height);
37329 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37333 Roo.bootstrap.layout.West = function(config){
37334 config.region = "west";
37335 config.cursor = "w-resize";
37337 Roo.bootstrap.layout.Split.call(this, config);
37339 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37340 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37341 this.split.el.addClass("roo-layout-split-h");
37345 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37346 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37348 onRender: function(ctr, pos)
37350 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37351 var size = this.config.initialSize || this.config.width;
37352 if(typeof size != "undefined"){
37353 this.el.setWidth(size);
37357 getBox : function(){
37358 if(this.collapsed){
37359 return this.collapsedEl.getBox();
37361 var box = this.el.getBox();
37363 box.width += this.split.el.getWidth();
37368 updateBox : function(box){
37369 if(this.split && !this.collapsed){
37370 var sw = this.split.el.getWidth();
37372 this.split.el.setLeft(box.x+box.width);
37373 this.split.el.setTop(box.y);
37374 this.split.el.setHeight(box.height);
37376 if(this.collapsed){
37377 this.updateBody(null, box.height);
37379 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37381 });Roo.namespace("Roo.bootstrap.panel");/*
37383 * Ext JS Library 1.1.1
37384 * Copyright(c) 2006-2007, Ext JS, LLC.
37386 * Originally Released Under LGPL - original licence link has changed is not relivant.
37389 * <script type="text/javascript">
37392 * @class Roo.ContentPanel
37393 * @extends Roo.util.Observable
37394 * A basic ContentPanel element.
37395 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37396 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37397 * @cfg {Boolean/Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
37398 * @cfg {Boolean} closable True if the panel can be closed/removed
37399 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37400 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37401 * @cfg {Toolbar} toolbar A toolbar for this panel
37402 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37403 * @cfg {String} title The title for this panel
37404 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37405 * @cfg {String} url Calls {@link #setUrl} with this value
37406 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37407 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37408 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37409 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37410 * @cfg {Boolean} badges render the badges
37413 * Create a new ContentPanel.
37414 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37415 * @param {String/Object} config A string to set only the title or a config object
37416 * @param {String} content (optional) Set the HTML content for this panel
37417 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37419 Roo.bootstrap.panel.Content = function( config){
37421 this.tpl = config.tpl || false;
37423 var el = config.el;
37424 var content = config.content;
37426 if(config.autoCreate){ // xtype is available if this is called from factory
37429 this.el = Roo.get(el);
37430 if(!this.el && config && config.autoCreate){
37431 if(typeof config.autoCreate == "object"){
37432 if(!config.autoCreate.id){
37433 config.autoCreate.id = config.id||el;
37435 this.el = Roo.DomHelper.append(document.body,
37436 config.autoCreate, true);
37438 var elcfg = { tag: "div",
37439 cls: "roo-layout-inactive-content",
37443 elcfg.html = config.html;
37447 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37450 this.closable = false;
37451 this.loaded = false;
37452 this.active = false;
37455 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37457 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37459 this.wrapEl = this.el; //this.el.wrap();
37461 if (config.toolbar.items) {
37462 ti = config.toolbar.items ;
37463 delete config.toolbar.items ;
37467 this.toolbar.render(this.wrapEl, 'before');
37468 for(var i =0;i < ti.length;i++) {
37469 // Roo.log(['add child', items[i]]);
37470 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37472 this.toolbar.items = nitems;
37473 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37474 delete config.toolbar;
37478 // xtype created footer. - not sure if will work as we normally have to render first..
37479 if (this.footer && !this.footer.el && this.footer.xtype) {
37480 if (!this.wrapEl) {
37481 this.wrapEl = this.el.wrap();
37484 this.footer.container = this.wrapEl.createChild();
37486 this.footer = Roo.factory(this.footer, Roo);
37491 if(typeof config == "string"){
37492 this.title = config;
37494 Roo.apply(this, config);
37498 this.resizeEl = Roo.get(this.resizeEl, true);
37500 this.resizeEl = this.el;
37502 // handle view.xtype
37510 * Fires when this panel is activated.
37511 * @param {Roo.ContentPanel} this
37515 * @event deactivate
37516 * Fires when this panel is activated.
37517 * @param {Roo.ContentPanel} this
37519 "deactivate" : true,
37523 * Fires when this panel is resized if fitToFrame is true.
37524 * @param {Roo.ContentPanel} this
37525 * @param {Number} width The width after any component adjustments
37526 * @param {Number} height The height after any component adjustments
37532 * Fires when this tab is created
37533 * @param {Roo.ContentPanel} this
37544 if(this.autoScroll){
37545 this.resizeEl.setStyle("overflow", "auto");
37547 // fix randome scrolling
37548 //this.el.on('scroll', function() {
37549 // Roo.log('fix random scolling');
37550 // this.scrollTo('top',0);
37553 content = content || this.content;
37555 this.setContent(content);
37557 if(config && config.url){
37558 this.setUrl(this.url, this.params, this.loadOnce);
37563 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37565 if (this.view && typeof(this.view.xtype) != 'undefined') {
37566 this.view.el = this.el.appendChild(document.createElement("div"));
37567 this.view = Roo.factory(this.view);
37568 this.view.render && this.view.render(false, '');
37572 this.fireEvent('render', this);
37575 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37579 setRegion : function(region){
37580 this.region = region;
37581 this.setActiveClass(region && !this.background);
37585 setActiveClass: function(state)
37588 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37589 this.el.setStyle('position','relative');
37591 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37592 this.el.setStyle('position', 'absolute');
37597 * Returns the toolbar for this Panel if one was configured.
37598 * @return {Roo.Toolbar}
37600 getToolbar : function(){
37601 return this.toolbar;
37604 setActiveState : function(active)
37606 this.active = active;
37607 this.setActiveClass(active);
37609 if(this.fireEvent("deactivate", this) === false){
37614 this.fireEvent("activate", this);
37618 * Updates this panel's element
37619 * @param {String} content The new content
37620 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37622 setContent : function(content, loadScripts){
37623 this.el.update(content, loadScripts);
37626 ignoreResize : function(w, h){
37627 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37630 this.lastSize = {width: w, height: h};
37635 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37636 * @return {Roo.UpdateManager} The UpdateManager
37638 getUpdateManager : function(){
37639 return this.el.getUpdateManager();
37642 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37643 * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
37646 url: "your-url.php",
37647 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37648 callback: yourFunction,
37649 scope: yourObject, //(optional scope)
37652 text: "Loading...",
37657 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37658 * are shorthand for <i>disableCaching</i>, <i>indicatorText</i> and <i>loadScripts</i> and are used to set their associated property on this panel UpdateManager instance.
37659 * @param {String/Object} params (optional) The parameters to pass as either a URL encoded string "param1=1&param2=2" or an object {param1: 1, param2: 2}
37660 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37661 * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
37662 * @return {Roo.ContentPanel} this
37665 var um = this.el.getUpdateManager();
37666 um.update.apply(um, arguments);
37672 * Set a URL to be used to load the content for this panel. When this panel is activated, the content will be loaded from that URL.
37673 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37674 * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
37675 * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this panel is activated. (Defaults to false)
37676 * @return {Roo.UpdateManager} The UpdateManager
37678 setUrl : function(url, params, loadOnce){
37679 if(this.refreshDelegate){
37680 this.removeListener("activate", this.refreshDelegate);
37682 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37683 this.on("activate", this.refreshDelegate);
37684 return this.el.getUpdateManager();
37687 _handleRefresh : function(url, params, loadOnce){
37688 if(!loadOnce || !this.loaded){
37689 var updater = this.el.getUpdateManager();
37690 updater.update(url, params, this._setLoaded.createDelegate(this));
37694 _setLoaded : function(){
37695 this.loaded = true;
37699 * Returns this panel's id
37702 getId : function(){
37707 * Returns this panel's element - used by regiosn to add.
37708 * @return {Roo.Element}
37710 getEl : function(){
37711 return this.wrapEl || this.el;
37716 adjustForComponents : function(width, height)
37718 //Roo.log('adjustForComponents ');
37719 if(this.resizeEl != this.el){
37720 width -= this.el.getFrameWidth('lr');
37721 height -= this.el.getFrameWidth('tb');
37724 var te = this.toolbar.getEl();
37725 te.setWidth(width);
37726 height -= te.getHeight();
37729 var te = this.footer.getEl();
37730 te.setWidth(width);
37731 height -= te.getHeight();
37735 if(this.adjustments){
37736 width += this.adjustments[0];
37737 height += this.adjustments[1];
37739 return {"width": width, "height": height};
37742 setSize : function(width, height){
37743 if(this.fitToFrame && !this.ignoreResize(width, height)){
37744 if(this.fitContainer && this.resizeEl != this.el){
37745 this.el.setSize(width, height);
37747 var size = this.adjustForComponents(width, height);
37748 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37749 this.fireEvent('resize', this, size.width, size.height);
37754 * Returns this panel's title
37757 getTitle : function(){
37759 if (typeof(this.title) != 'object') {
37764 for (var k in this.title) {
37765 if (!this.title.hasOwnProperty(k)) {
37769 if (k.indexOf('-') >= 0) {
37770 var s = k.split('-');
37771 for (var i = 0; i<s.length; i++) {
37772 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37775 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37782 * Set this panel's title
37783 * @param {String} title
37785 setTitle : function(title){
37786 this.title = title;
37788 this.region.updatePanelTitle(this, title);
37793 * Returns true is this panel was configured to be closable
37794 * @return {Boolean}
37796 isClosable : function(){
37797 return this.closable;
37800 beforeSlide : function(){
37802 this.resizeEl.clip();
37805 afterSlide : function(){
37807 this.resizeEl.unclip();
37811 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37812 * Will fail silently if the {@link #setUrl} method has not been called.
37813 * This does not activate the panel, just updates its content.
37815 refresh : function(){
37816 if(this.refreshDelegate){
37817 this.loaded = false;
37818 this.refreshDelegate();
37823 * Destroys this panel
37825 destroy : function(){
37826 this.el.removeAllListeners();
37827 var tempEl = document.createElement("span");
37828 tempEl.appendChild(this.el.dom);
37829 tempEl.innerHTML = "";
37835 * form - if the content panel contains a form - this is a reference to it.
37836 * @type {Roo.form.Form}
37840 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37841 * This contains a reference to it.
37847 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37857 * @param {Object} cfg Xtype definition of item to add.
37861 getChildContainer: function () {
37862 return this.getEl();
37867 var ret = new Roo.factory(cfg);
37872 if (cfg.xtype.match(/^Form$/)) {
37875 //if (this.footer) {
37876 // el = this.footer.container.insertSibling(false, 'before');
37878 el = this.el.createChild();
37881 this.form = new Roo.form.Form(cfg);
37884 if ( this.form.allItems.length) {
37885 this.form.render(el.dom);
37889 // should only have one of theses..
37890 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37891 // views.. should not be just added - used named prop 'view''
37893 cfg.el = this.el.appendChild(document.createElement("div"));
37896 var ret = new Roo.factory(cfg);
37898 ret.render && ret.render(false, ''); // render blank..
37908 * @class Roo.bootstrap.panel.Grid
37909 * @extends Roo.bootstrap.panel.Content
37911 * Create a new GridPanel.
37912 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37913 * @param {Object} config A the config object
37919 Roo.bootstrap.panel.Grid = function(config)
37923 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37924 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37926 config.el = this.wrapper;
37927 //this.el = this.wrapper;
37929 if (config.container) {
37930 // ctor'ed from a Border/panel.grid
37933 this.wrapper.setStyle("overflow", "hidden");
37934 this.wrapper.addClass('roo-grid-container');
37939 if(config.toolbar){
37940 var tool_el = this.wrapper.createChild();
37941 this.toolbar = Roo.factory(config.toolbar);
37943 if (config.toolbar.items) {
37944 ti = config.toolbar.items ;
37945 delete config.toolbar.items ;
37949 this.toolbar.render(tool_el);
37950 for(var i =0;i < ti.length;i++) {
37951 // Roo.log(['add child', items[i]]);
37952 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37954 this.toolbar.items = nitems;
37956 delete config.toolbar;
37959 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37960 config.grid.scrollBody = true;;
37961 config.grid.monitorWindowResize = false; // turn off autosizing
37962 config.grid.autoHeight = false;
37963 config.grid.autoWidth = false;
37965 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37967 if (config.background) {
37968 // render grid on panel activation (if panel background)
37969 this.on('activate', function(gp) {
37970 if (!gp.grid.rendered) {
37971 gp.grid.render(this.wrapper);
37972 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37977 this.grid.render(this.wrapper);
37978 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37981 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37982 // ??? needed ??? config.el = this.wrapper;
37987 // xtype created footer. - not sure if will work as we normally have to render first..
37988 if (this.footer && !this.footer.el && this.footer.xtype) {
37990 var ctr = this.grid.getView().getFooterPanel(true);
37991 this.footer.dataSource = this.grid.dataSource;
37992 this.footer = Roo.factory(this.footer, Roo);
37993 this.footer.render(ctr);
38003 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38004 getId : function(){
38005 return this.grid.id;
38009 * Returns the grid for this panel
38010 * @return {Roo.bootstrap.Table}
38012 getGrid : function(){
38016 setSize : function(width, height){
38017 if(!this.ignoreResize(width, height)){
38018 var grid = this.grid;
38019 var size = this.adjustForComponents(width, height);
38020 var gridel = grid.getGridEl();
38021 gridel.setSize(size.width, size.height);
38023 var thd = grid.getGridEl().select('thead',true).first();
38024 var tbd = grid.getGridEl().select('tbody', true).first();
38026 tbd.setSize(width, height - thd.getHeight());
38035 beforeSlide : function(){
38036 this.grid.getView().scroller.clip();
38039 afterSlide : function(){
38040 this.grid.getView().scroller.unclip();
38043 destroy : function(){
38044 this.grid.destroy();
38046 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
38051 * @class Roo.bootstrap.panel.Nest
38052 * @extends Roo.bootstrap.panel.Content
38054 * Create a new Panel, that can contain a layout.Border.
38057 * @param {Roo.BorderLayout} layout The layout for this panel
38058 * @param {String/Object} config A string to set only the title or a config object
38060 Roo.bootstrap.panel.Nest = function(config)
38062 // construct with only one argument..
38063 /* FIXME - implement nicer consturctors
38064 if (layout.layout) {
38066 layout = config.layout;
38067 delete config.layout;
38069 if (layout.xtype && !layout.getEl) {
38070 // then layout needs constructing..
38071 layout = Roo.factory(layout, Roo);
38075 config.el = config.layout.getEl();
38077 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38079 config.layout.monitorWindowResize = false; // turn off autosizing
38080 this.layout = config.layout;
38081 this.layout.getEl().addClass("roo-layout-nested-layout");
38082 this.layout.parent = this;
38089 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38091 setSize : function(width, height){
38092 if(!this.ignoreResize(width, height)){
38093 var size = this.adjustForComponents(width, height);
38094 var el = this.layout.getEl();
38095 if (size.height < 1) {
38096 el.setWidth(size.width);
38098 el.setSize(size.width, size.height);
38100 var touch = el.dom.offsetWidth;
38101 this.layout.layout();
38102 // ie requires a double layout on the first pass
38103 if(Roo.isIE && !this.initialized){
38104 this.initialized = true;
38105 this.layout.layout();
38110 // activate all subpanels if not currently active..
38112 setActiveState : function(active){
38113 this.active = active;
38114 this.setActiveClass(active);
38117 this.fireEvent("deactivate", this);
38121 this.fireEvent("activate", this);
38122 // not sure if this should happen before or after..
38123 if (!this.layout) {
38124 return; // should not happen..
38127 for (var r in this.layout.regions) {
38128 reg = this.layout.getRegion(r);
38129 if (reg.getActivePanel()) {
38130 //reg.showPanel(reg.getActivePanel()); // force it to activate..
38131 reg.setActivePanel(reg.getActivePanel());
38134 if (!reg.panels.length) {
38137 reg.showPanel(reg.getPanel(0));
38146 * Returns the nested BorderLayout for this panel
38147 * @return {Roo.BorderLayout}
38149 getLayout : function(){
38150 return this.layout;
38154 * Adds a xtype elements to the layout of the nested panel
38158 xtype : 'ContentPanel',
38165 xtype : 'NestedLayoutPanel',
38171 items : [ ... list of content panels or nested layout panels.. ]
38175 * @param {Object} cfg Xtype definition of item to add.
38177 addxtype : function(cfg) {
38178 return this.layout.addxtype(cfg);
38183 * Ext JS Library 1.1.1
38184 * Copyright(c) 2006-2007, Ext JS, LLC.
38186 * Originally Released Under LGPL - original licence link has changed is not relivant.
38189 * <script type="text/javascript">
38192 * @class Roo.TabPanel
38193 * @extends Roo.util.Observable
38194 * A lightweight tab container.
38198 // basic tabs 1, built from existing content
38199 var tabs = new Roo.TabPanel("tabs1");
38200 tabs.addTab("script", "View Script");
38201 tabs.addTab("markup", "View Markup");
38202 tabs.activate("script");
38204 // more advanced tabs, built from javascript
38205 var jtabs = new Roo.TabPanel("jtabs");
38206 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38208 // set up the UpdateManager
38209 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38210 var updater = tab2.getUpdateManager();
38211 updater.setDefaultUrl("ajax1.htm");
38212 tab2.on('activate', updater.refresh, updater, true);
38214 // Use setUrl for Ajax loading
38215 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38216 tab3.setUrl("ajax2.htm", null, true);
38219 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38222 jtabs.activate("jtabs-1");
38225 * Create a new TabPanel.
38226 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38227 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38229 Roo.bootstrap.panel.Tabs = function(config){
38231 * The container element for this TabPanel.
38232 * @type Roo.Element
38234 this.el = Roo.get(config.el);
38237 if(typeof config == "boolean"){
38238 this.tabPosition = config ? "bottom" : "top";
38240 Roo.apply(this, config);
38244 if(this.tabPosition == "bottom"){
38245 // if tabs are at the bottom = create the body first.
38246 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38247 this.el.addClass("roo-tabs-bottom");
38249 // next create the tabs holders
38251 if (this.tabPosition == "west"){
38253 var reg = this.region; // fake it..
38255 if (!reg.mgr.parent) {
38258 reg = reg.mgr.parent.region;
38260 Roo.log("got nest?");
38262 if (reg.mgr.getRegion('west')) {
38263 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38264 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38265 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38266 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38267 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38275 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38276 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38277 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38278 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38283 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38286 // finally - if tabs are at the top, then create the body last..
38287 if(this.tabPosition != "bottom"){
38288 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38289 * @type Roo.Element
38291 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38292 this.el.addClass("roo-tabs-top");
38296 this.bodyEl.setStyle("position", "relative");
38298 this.active = null;
38299 this.activateDelegate = this.activate.createDelegate(this);
38304 * Fires when the active tab changes
38305 * @param {Roo.TabPanel} this
38306 * @param {Roo.TabPanelItem} activePanel The new active tab
38310 * @event beforetabchange
38311 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38312 * @param {Roo.TabPanel} this
38313 * @param {Object} e Set cancel to true on this object to cancel the tab change
38314 * @param {Roo.TabPanelItem} tab The tab being changed to
38316 "beforetabchange" : true
38319 Roo.EventManager.onWindowResize(this.onResize, this);
38320 this.cpad = this.el.getPadding("lr");
38321 this.hiddenCount = 0;
38324 // toolbar on the tabbar support...
38325 if (this.toolbar) {
38326 alert("no toolbar support yet");
38327 this.toolbar = false;
38329 var tcfg = this.toolbar;
38330 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
38331 this.toolbar = new Roo.Toolbar(tcfg);
38332 if (Roo.isSafari) {
38333 var tbl = tcfg.container.child('table', true);
38334 tbl.setAttribute('width', '100%');
38342 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38345 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38347 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38349 tabPosition : "top",
38351 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38353 currentTabWidth : 0,
38355 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38359 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38363 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38365 preferredTabWidth : 175,
38367 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38369 resizeTabs : false,
38371 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38373 monitorResize : true,
38375 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
38377 toolbar : false, // set by caller..
38379 region : false, /// set by caller
38381 disableTooltips : true, // not used yet...
38384 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38385 * @param {String} id The id of the div to use <b>or create</b>
38386 * @param {String} text The text for the tab
38387 * @param {String} content (optional) Content to put in the TabPanelItem body
38388 * @param {Boolean} closable (optional) True to create a close icon on the tab
38389 * @return {Roo.TabPanelItem} The created TabPanelItem
38391 addTab : function(id, text, content, closable, tpl)
38393 var item = new Roo.bootstrap.panel.TabItem({
38397 closable : closable,
38400 this.addTabItem(item);
38402 item.setContent(content);
38408 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38409 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38410 * @return {Roo.TabPanelItem}
38412 getTab : function(id){
38413 return this.items[id];
38417 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38418 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38420 hideTab : function(id){
38421 var t = this.items[id];
38424 this.hiddenCount++;
38425 this.autoSizeTabs();
38430 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38431 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38433 unhideTab : function(id){
38434 var t = this.items[id];
38436 t.setHidden(false);
38437 this.hiddenCount--;
38438 this.autoSizeTabs();
38443 * Adds an existing {@link Roo.TabPanelItem}.
38444 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38446 addTabItem : function(item)
38448 this.items[item.id] = item;
38449 this.items.push(item);
38450 this.autoSizeTabs();
38451 // if(this.resizeTabs){
38452 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38453 // this.autoSizeTabs();
38455 // item.autoSize();
38460 * Removes a {@link Roo.TabPanelItem}.
38461 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38463 removeTab : function(id){
38464 var items = this.items;
38465 var tab = items[id];
38466 if(!tab) { return; }
38467 var index = items.indexOf(tab);
38468 if(this.active == tab && items.length > 1){
38469 var newTab = this.getNextAvailable(index);
38474 this.stripEl.dom.removeChild(tab.pnode.dom);
38475 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38476 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38478 items.splice(index, 1);
38479 delete this.items[tab.id];
38480 tab.fireEvent("close", tab);
38481 tab.purgeListeners();
38482 this.autoSizeTabs();
38485 getNextAvailable : function(start){
38486 var items = this.items;
38488 // look for a next tab that will slide over to
38489 // replace the one being removed
38490 while(index < items.length){
38491 var item = items[++index];
38492 if(item && !item.isHidden()){
38496 // if one isn't found select the previous tab (on the left)
38499 var item = items[--index];
38500 if(item && !item.isHidden()){
38508 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38509 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38511 disableTab : function(id){
38512 var tab = this.items[id];
38513 if(tab && this.active != tab){
38519 * Enables a {@link Roo.TabPanelItem} that is disabled.
38520 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38522 enableTab : function(id){
38523 var tab = this.items[id];
38528 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38529 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38530 * @return {Roo.TabPanelItem} The TabPanelItem.
38532 activate : function(id)
38534 //Roo.log('activite:' + id);
38536 var tab = this.items[id];
38540 if(tab == this.active || tab.disabled){
38544 this.fireEvent("beforetabchange", this, e, tab);
38545 if(e.cancel !== true && !tab.disabled){
38547 this.active.hide();
38549 this.active = this.items[id];
38550 this.active.show();
38551 this.fireEvent("tabchange", this, this.active);
38557 * Gets the active {@link Roo.TabPanelItem}.
38558 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38560 getActiveTab : function(){
38561 return this.active;
38565 * Updates the tab body element to fit the height of the container element
38566 * for overflow scrolling
38567 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38569 syncHeight : function(targetHeight){
38570 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38571 var bm = this.bodyEl.getMargins();
38572 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38573 this.bodyEl.setHeight(newHeight);
38577 onResize : function(){
38578 if(this.monitorResize){
38579 this.autoSizeTabs();
38584 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38586 beginUpdate : function(){
38587 this.updating = true;
38591 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38593 endUpdate : function(){
38594 this.updating = false;
38595 this.autoSizeTabs();
38599 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38601 autoSizeTabs : function()
38603 var count = this.items.length;
38604 var vcount = count - this.hiddenCount;
38607 this.stripEl.hide();
38609 this.stripEl.show();
38612 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38617 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38618 var availWidth = Math.floor(w / vcount);
38619 var b = this.stripBody;
38620 if(b.getWidth() > w){
38621 var tabs = this.items;
38622 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38623 if(availWidth < this.minTabWidth){
38624 /*if(!this.sleft){ // incomplete scrolling code
38625 this.createScrollButtons();
38628 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38631 if(this.currentTabWidth < this.preferredTabWidth){
38632 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38638 * Returns the number of tabs in this TabPanel.
38641 getCount : function(){
38642 return this.items.length;
38646 * Resizes all the tabs to the passed width
38647 * @param {Number} The new width
38649 setTabWidth : function(width){
38650 this.currentTabWidth = width;
38651 for(var i = 0, len = this.items.length; i < len; i++) {
38652 if(!this.items[i].isHidden()) {
38653 this.items[i].setWidth(width);
38659 * Destroys this TabPanel
38660 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38662 destroy : function(removeEl){
38663 Roo.EventManager.removeResizeListener(this.onResize, this);
38664 for(var i = 0, len = this.items.length; i < len; i++){
38665 this.items[i].purgeListeners();
38667 if(removeEl === true){
38668 this.el.update("");
38673 createStrip : function(container)
38675 var strip = document.createElement("nav");
38676 strip.className = Roo.bootstrap.version == 4 ?
38677 "navbar-light bg-light" :
38678 "navbar navbar-default"; //"x-tabs-wrap";
38679 container.appendChild(strip);
38683 createStripList : function(strip)
38685 // div wrapper for retard IE
38686 // returns the "tr" element.
38687 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38688 //'<div class="x-tabs-strip-wrap">'+
38689 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38690 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38691 return strip.firstChild; //.firstChild.firstChild.firstChild;
38693 createBody : function(container)
38695 var body = document.createElement("div");
38696 Roo.id(body, "tab-body");
38697 //Roo.fly(body).addClass("x-tabs-body");
38698 Roo.fly(body).addClass("tab-content");
38699 container.appendChild(body);
38702 createItemBody :function(bodyEl, id){
38703 var body = Roo.getDom(id);
38705 body = document.createElement("div");
38708 //Roo.fly(body).addClass("x-tabs-item-body");
38709 Roo.fly(body).addClass("tab-pane");
38710 bodyEl.insertBefore(body, bodyEl.firstChild);
38714 createStripElements : function(stripEl, text, closable, tpl)
38716 var td = document.createElement("li"); // was td..
38717 td.className = 'nav-item';
38719 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38722 stripEl.appendChild(td);
38724 td.className = "x-tabs-closable";
38725 if(!this.closeTpl){
38726 this.closeTpl = new Roo.Template(
38727 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38728 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38729 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38732 var el = this.closeTpl.overwrite(td, {"text": text});
38733 var close = el.getElementsByTagName("div")[0];
38734 var inner = el.getElementsByTagName("em")[0];
38735 return {"el": el, "close": close, "inner": inner};
38738 // not sure what this is..
38739 // if(!this.tabTpl){
38740 //this.tabTpl = new Roo.Template(
38741 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38742 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38744 // this.tabTpl = new Roo.Template(
38745 // '<a href="#">' +
38746 // '<span unselectable="on"' +
38747 // (this.disableTooltips ? '' : ' title="{text}"') +
38748 // ' >{text}</span></a>'
38754 var template = tpl || this.tabTpl || false;
38757 template = new Roo.Template(
38758 Roo.bootstrap.version == 4 ?
38760 '<a class="nav-link" href="#" unselectable="on"' +
38761 (this.disableTooltips ? '' : ' title="{text}"') +
38764 '<a class="nav-link" href="#">' +
38765 '<span unselectable="on"' +
38766 (this.disableTooltips ? '' : ' title="{text}"') +
38767 ' >{text}</span></a>'
38772 switch (typeof(template)) {
38776 template = new Roo.Template(template);
38782 var el = template.overwrite(td, {"text": text});
38784 var inner = el.getElementsByTagName("span")[0];
38786 return {"el": el, "inner": inner};
38794 * @class Roo.TabPanelItem
38795 * @extends Roo.util.Observable
38796 * Represents an individual item (tab plus body) in a TabPanel.
38797 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38798 * @param {String} id The id of this TabPanelItem
38799 * @param {String} text The text for the tab of this TabPanelItem
38800 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38802 Roo.bootstrap.panel.TabItem = function(config){
38804 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38805 * @type Roo.TabPanel
38807 this.tabPanel = config.panel;
38809 * The id for this TabPanelItem
38812 this.id = config.id;
38814 this.disabled = false;
38816 this.text = config.text;
38818 this.loaded = false;
38819 this.closable = config.closable;
38822 * The body element for this TabPanelItem.
38823 * @type Roo.Element
38825 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38826 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38827 this.bodyEl.setStyle("display", "block");
38828 this.bodyEl.setStyle("zoom", "1");
38829 //this.hideAction();
38831 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38833 this.el = Roo.get(els.el);
38834 this.inner = Roo.get(els.inner, true);
38835 this.textEl = Roo.bootstrap.version == 4 ?
38836 this.el : Roo.get(this.el.dom.firstChild, true);
38838 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38839 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38842 // this.el.on("mousedown", this.onTabMouseDown, this);
38843 this.el.on("click", this.onTabClick, this);
38845 if(config.closable){
38846 var c = Roo.get(els.close, true);
38847 c.dom.title = this.closeText;
38848 c.addClassOnOver("close-over");
38849 c.on("click", this.closeClick, this);
38855 * Fires when this tab becomes the active tab.
38856 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38857 * @param {Roo.TabPanelItem} this
38861 * @event beforeclose
38862 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38863 * @param {Roo.TabPanelItem} this
38864 * @param {Object} e Set cancel to true on this object to cancel the close.
38866 "beforeclose": true,
38869 * Fires when this tab is closed.
38870 * @param {Roo.TabPanelItem} this
38874 * @event deactivate
38875 * Fires when this tab is no longer the active tab.
38876 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38877 * @param {Roo.TabPanelItem} this
38879 "deactivate" : true
38881 this.hidden = false;
38883 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38886 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38888 purgeListeners : function(){
38889 Roo.util.Observable.prototype.purgeListeners.call(this);
38890 this.el.removeAllListeners();
38893 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38896 this.status_node.addClass("active");
38899 this.tabPanel.stripWrap.repaint();
38901 this.fireEvent("activate", this.tabPanel, this);
38905 * Returns true if this tab is the active tab.
38906 * @return {Boolean}
38908 isActive : function(){
38909 return this.tabPanel.getActiveTab() == this;
38913 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38916 this.status_node.removeClass("active");
38918 this.fireEvent("deactivate", this.tabPanel, this);
38921 hideAction : function(){
38922 this.bodyEl.hide();
38923 this.bodyEl.setStyle("position", "absolute");
38924 this.bodyEl.setLeft("-20000px");
38925 this.bodyEl.setTop("-20000px");
38928 showAction : function(){
38929 this.bodyEl.setStyle("position", "relative");
38930 this.bodyEl.setTop("");
38931 this.bodyEl.setLeft("");
38932 this.bodyEl.show();
38936 * Set the tooltip for the tab.
38937 * @param {String} tooltip The tab's tooltip
38939 setTooltip : function(text){
38940 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38941 this.textEl.dom.qtip = text;
38942 this.textEl.dom.removeAttribute('title');
38944 this.textEl.dom.title = text;
38948 onTabClick : function(e){
38949 e.preventDefault();
38950 this.tabPanel.activate(this.id);
38953 onTabMouseDown : function(e){
38954 e.preventDefault();
38955 this.tabPanel.activate(this.id);
38958 getWidth : function(){
38959 return this.inner.getWidth();
38962 setWidth : function(width){
38963 var iwidth = width - this.linode.getPadding("lr");
38964 this.inner.setWidth(iwidth);
38965 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38966 this.linode.setWidth(width);
38970 * Show or hide the tab
38971 * @param {Boolean} hidden True to hide or false to show.
38973 setHidden : function(hidden){
38974 this.hidden = hidden;
38975 this.linode.setStyle("display", hidden ? "none" : "");
38979 * Returns true if this tab is "hidden"
38980 * @return {Boolean}
38982 isHidden : function(){
38983 return this.hidden;
38987 * Returns the text for this tab
38990 getText : function(){
38994 autoSize : function(){
38995 //this.el.beginMeasure();
38996 this.textEl.setWidth(1);
38998 * #2804 [new] Tabs in Roojs
38999 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39001 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39002 //this.el.endMeasure();
39006 * Sets the text for the tab (Note: this also sets the tooltip text)
39007 * @param {String} text The tab's text and tooltip
39009 setText : function(text){
39011 this.textEl.update(text);
39012 this.setTooltip(text);
39013 //if(!this.tabPanel.resizeTabs){
39014 // this.autoSize();
39018 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39020 activate : function(){
39021 this.tabPanel.activate(this.id);
39025 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39027 disable : function(){
39028 if(this.tabPanel.active != this){
39029 this.disabled = true;
39030 this.status_node.addClass("disabled");
39035 * Enables this TabPanelItem if it was previously disabled.
39037 enable : function(){
39038 this.disabled = false;
39039 this.status_node.removeClass("disabled");
39043 * Sets the content for this TabPanelItem.
39044 * @param {String} content The content
39045 * @param {Boolean} loadScripts true to look for and load scripts
39047 setContent : function(content, loadScripts){
39048 this.bodyEl.update(content, loadScripts);
39052 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39053 * @return {Roo.UpdateManager} The UpdateManager
39055 getUpdateManager : function(){
39056 return this.bodyEl.getUpdateManager();
39060 * Set a URL to be used to load the content for this TabPanelItem.
39061 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39062 * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
39063 * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
39064 * @return {Roo.UpdateManager} The UpdateManager
39066 setUrl : function(url, params, loadOnce){
39067 if(this.refreshDelegate){
39068 this.un('activate', this.refreshDelegate);
39070 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39071 this.on("activate", this.refreshDelegate);
39072 return this.bodyEl.getUpdateManager();
39076 _handleRefresh : function(url, params, loadOnce){
39077 if(!loadOnce || !this.loaded){
39078 var updater = this.bodyEl.getUpdateManager();
39079 updater.update(url, params, this._setLoaded.createDelegate(this));
39084 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
39085 * Will fail silently if the setUrl method has not been called.
39086 * This does not activate the panel, just updates its content.
39088 refresh : function(){
39089 if(this.refreshDelegate){
39090 this.loaded = false;
39091 this.refreshDelegate();
39096 _setLoaded : function(){
39097 this.loaded = true;
39101 closeClick : function(e){
39104 this.fireEvent("beforeclose", this, o);
39105 if(o.cancel !== true){
39106 this.tabPanel.removeTab(this.id);
39110 * The text displayed in the tooltip for the close icon.
39113 closeText : "Close this tab"
39116 * This script refer to:
39117 * Title: International Telephone Input
39118 * Author: Jack O'Connor
39119 * Code version: v12.1.12
39120 * Availability: https://github.com/jackocnr/intl-tel-input.git
39123 Roo.bootstrap.PhoneInputData = function() {
39126 "Afghanistan (افغانستان)",
39131 "Albania (Shqipëri)",
39136 "Algeria (الجزائر)",
39161 "Antigua and Barbuda",
39171 "Armenia (Հայաստան)",
39187 "Austria (Österreich)",
39192 "Azerbaijan (Azərbaycan)",
39202 "Bahrain (البحرين)",
39207 "Bangladesh (বাংলাদেশ)",
39217 "Belarus (Беларусь)",
39222 "Belgium (België)",
39252 "Bosnia and Herzegovina (Босна и Херцеговина)",
39267 "British Indian Ocean Territory",
39272 "British Virgin Islands",
39282 "Bulgaria (България)",
39292 "Burundi (Uburundi)",
39297 "Cambodia (កម្ពុជា)",
39302 "Cameroon (Cameroun)",
39311 ["204", "226", "236", "249", "250", "289", "306", "343", "365", "387", "403", "416", "418", "431", "437", "438", "450", "506", "514", "519", "548", "579", "581", "587", "604", "613", "639", "647", "672", "705", "709", "742", "778", "780", "782", "807", "819", "825", "867", "873", "902", "905"]
39314 "Cape Verde (Kabu Verdi)",
39319 "Caribbean Netherlands",
39330 "Central African Republic (République centrafricaine)",
39350 "Christmas Island",
39356 "Cocos (Keeling) Islands",
39367 "Comoros (جزر القمر)",
39372 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39377 "Congo (Republic) (Congo-Brazzaville)",
39397 "Croatia (Hrvatska)",
39418 "Czech Republic (Česká republika)",
39423 "Denmark (Danmark)",
39438 "Dominican Republic (República Dominicana)",
39442 ["809", "829", "849"]
39460 "Equatorial Guinea (Guinea Ecuatorial)",
39480 "Falkland Islands (Islas Malvinas)",
39485 "Faroe Islands (Føroyar)",
39506 "French Guiana (Guyane française)",
39511 "French Polynesia (Polynésie française)",
39526 "Georgia (საქართველო)",
39531 "Germany (Deutschland)",
39551 "Greenland (Kalaallit Nunaat)",
39588 "Guinea-Bissau (Guiné Bissau)",
39613 "Hungary (Magyarország)",
39618 "Iceland (Ísland)",
39638 "Iraq (العراق)",
39654 "Israel (ישראל)",
39681 "Jordan (الأردن)",
39686 "Kazakhstan (Казахстан)",
39707 "Kuwait (الكويت)",
39712 "Kyrgyzstan (Кыргызстан)",
39722 "Latvia (Latvija)",
39727 "Lebanon (لبنان)",
39742 "Libya (ليبيا)",
39752 "Lithuania (Lietuva)",
39767 "Macedonia (FYROM) (Македонија)",
39772 "Madagascar (Madagasikara)",
39802 "Marshall Islands",
39812 "Mauritania (موريتانيا)",
39817 "Mauritius (Moris)",
39838 "Moldova (Republica Moldova)",
39848 "Mongolia (Монгол)",
39853 "Montenegro (Crna Gora)",
39863 "Morocco (المغرب)",
39869 "Mozambique (Moçambique)",
39874 "Myanmar (Burma) (မြန်မာ)",
39879 "Namibia (Namibië)",
39894 "Netherlands (Nederland)",
39899 "New Caledonia (Nouvelle-Calédonie)",
39934 "North Korea (조선 민주주의 인민 공화국)",
39939 "Northern Mariana Islands",
39955 "Pakistan (پاکستان)",
39965 "Palestine (فلسطين)",
39975 "Papua New Guinea",
40017 "Réunion (La Réunion)",
40023 "Romania (România)",
40039 "Saint Barthélemy",
40050 "Saint Kitts and Nevis",
40060 "Saint Martin (Saint-Martin (partie française))",
40066 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40071 "Saint Vincent and the Grenadines",
40086 "São Tomé and Príncipe (São Tomé e Príncipe)",
40091 "Saudi Arabia (المملكة العربية السعودية)",
40096 "Senegal (Sénégal)",
40126 "Slovakia (Slovensko)",
40131 "Slovenia (Slovenija)",
40141 "Somalia (Soomaaliya)",
40151 "South Korea (대한민국)",
40156 "South Sudan (جنوب السودان)",
40166 "Sri Lanka (ශ්රී ලංකාව)",
40171 "Sudan (السودان)",
40181 "Svalbard and Jan Mayen",
40192 "Sweden (Sverige)",
40197 "Switzerland (Schweiz)",
40202 "Syria (سوريا)",
40247 "Trinidad and Tobago",
40252 "Tunisia (تونس)",
40257 "Turkey (Türkiye)",
40267 "Turks and Caicos Islands",
40277 "U.S. Virgin Islands",
40287 "Ukraine (Україна)",
40292 "United Arab Emirates (الإمارات العربية المتحدة)",
40314 "Uzbekistan (Oʻzbekiston)",
40324 "Vatican City (Città del Vaticano)",
40335 "Vietnam (Việt Nam)",
40340 "Wallis and Futuna (Wallis-et-Futuna)",
40345 "Western Sahara (الصحراء الغربية)",
40351 "Yemen (اليمن)",
40375 * This script refer to:
40376 * Title: International Telephone Input
40377 * Author: Jack O'Connor
40378 * Code version: v12.1.12
40379 * Availability: https://github.com/jackocnr/intl-tel-input.git
40383 * @class Roo.bootstrap.PhoneInput
40384 * @extends Roo.bootstrap.TriggerField
40385 * An input with International dial-code selection
40387 * @cfg {String} defaultDialCode default '+852'
40388 * @cfg {Array} preferedCountries default []
40391 * Create a new PhoneInput.
40392 * @param {Object} config Configuration options
40395 Roo.bootstrap.PhoneInput = function(config) {
40396 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40399 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40401 listWidth: undefined,
40403 selectedClass: 'active',
40405 invalidClass : "has-warning",
40407 validClass: 'has-success',
40409 allowed: '0123456789',
40414 * @cfg {String} defaultDialCode The default dial code when initializing the input
40416 defaultDialCode: '+852',
40419 * @cfg {Array} preferedCountries A list of iso2 in array (e.g. ['hk','us']). Those related countries will show at the top of the input's choices
40421 preferedCountries: false,
40423 getAutoCreate : function()
40425 var data = Roo.bootstrap.PhoneInputData();
40426 var align = this.labelAlign || this.parentLabelAlign();
40429 this.allCountries = [];
40430 this.dialCodeMapping = [];
40432 for (var i = 0; i < data.length; i++) {
40434 this.allCountries[i] = {
40438 priority: c[3] || 0,
40439 areaCodes: c[4] || null
40441 this.dialCodeMapping[c[2]] = {
40444 priority: c[3] || 0,
40445 areaCodes: c[4] || null
40457 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40458 maxlength: this.max_length,
40459 cls : 'form-control tel-input',
40460 autocomplete: 'new-password'
40463 var hiddenInput = {
40466 cls: 'hidden-tel-input'
40470 hiddenInput.name = this.name;
40473 if (this.disabled) {
40474 input.disabled = true;
40477 var flag_container = {
40494 cls: this.hasFeedback ? 'has-feedback' : '',
40500 cls: 'dial-code-holder',
40507 cls: 'roo-select2-container input-group',
40514 if (this.fieldLabel.length) {
40517 tooltip: 'This field is required'
40523 cls: 'control-label',
40529 html: this.fieldLabel
40532 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40538 if(this.indicatorpos == 'right') {
40539 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40546 if(align == 'left') {
40554 if(this.labelWidth > 12){
40555 label.style = "width: " + this.labelWidth + 'px';
40557 if(this.labelWidth < 13 && this.labelmd == 0){
40558 this.labelmd = this.labelWidth;
40560 if(this.labellg > 0){
40561 label.cls += ' col-lg-' + this.labellg;
40562 input.cls += ' col-lg-' + (12 - this.labellg);
40564 if(this.labelmd > 0){
40565 label.cls += ' col-md-' + this.labelmd;
40566 container.cls += ' col-md-' + (12 - this.labelmd);
40568 if(this.labelsm > 0){
40569 label.cls += ' col-sm-' + this.labelsm;
40570 container.cls += ' col-sm-' + (12 - this.labelsm);
40572 if(this.labelxs > 0){
40573 label.cls += ' col-xs-' + this.labelxs;
40574 container.cls += ' col-xs-' + (12 - this.labelxs);
40584 var settings = this;
40586 ['xs','sm','md','lg'].map(function(size){
40587 if (settings[size]) {
40588 cfg.cls += ' col-' + size + '-' + settings[size];
40592 this.store = new Roo.data.Store({
40593 proxy : new Roo.data.MemoryProxy({}),
40594 reader : new Roo.data.JsonReader({
40605 'name' : 'dialCode',
40609 'name' : 'priority',
40613 'name' : 'areaCodes',
40620 if(!this.preferedCountries) {
40621 this.preferedCountries = [
40628 var p = this.preferedCountries.reverse();
40631 for (var i = 0; i < p.length; i++) {
40632 for (var j = 0; j < this.allCountries.length; j++) {
40633 if(this.allCountries[j].iso2 == p[i]) {
40634 var t = this.allCountries[j];
40635 this.allCountries.splice(j,1);
40636 this.allCountries.unshift(t);
40642 this.store.proxy.data = {
40644 data: this.allCountries
40650 initEvents : function()
40653 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40655 this.indicator = this.indicatorEl();
40656 this.flag = this.flagEl();
40657 this.dialCodeHolder = this.dialCodeHolderEl();
40659 this.trigger = this.el.select('div.flag-box',true).first();
40660 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40665 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40666 _this.list.setWidth(lw);
40669 this.list.on('mouseover', this.onViewOver, this);
40670 this.list.on('mousemove', this.onViewMove, this);
40671 this.inputEl().on("keyup", this.onKeyUp, this);
40672 this.inputEl().on("keypress", this.onKeyPress, this);
40674 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40676 this.view = new Roo.View(this.list, this.tpl, {
40677 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40680 this.view.on('click', this.onViewClick, this);
40681 this.setValue(this.defaultDialCode);
40684 onTriggerClick : function(e)
40686 Roo.log('trigger click');
40691 if(this.isExpanded()){
40693 this.hasFocus = false;
40695 this.store.load({});
40696 this.hasFocus = true;
40701 isExpanded : function()
40703 return this.list.isVisible();
40706 collapse : function()
40708 if(!this.isExpanded()){
40712 Roo.get(document).un('mousedown', this.collapseIf, this);
40713 Roo.get(document).un('mousewheel', this.collapseIf, this);
40714 this.fireEvent('collapse', this);
40718 expand : function()
40722 if(this.isExpanded() || !this.hasFocus){
40726 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40727 this.list.setWidth(lw);
40730 this.restrictHeight();
40732 Roo.get(document).on('mousedown', this.collapseIf, this);
40733 Roo.get(document).on('mousewheel', this.collapseIf, this);
40735 this.fireEvent('expand', this);
40738 restrictHeight : function()
40740 this.list.alignTo(this.inputEl(), this.listAlign);
40741 this.list.alignTo(this.inputEl(), this.listAlign);
40744 onViewOver : function(e, t)
40746 if(this.inKeyMode){
40749 var item = this.view.findItemFromChild(t);
40752 var index = this.view.indexOf(item);
40753 this.select(index, false);
40758 onViewClick : function(view, doFocus, el, e)
40760 var index = this.view.getSelectedIndexes()[0];
40762 var r = this.store.getAt(index);
40765 this.onSelect(r, index);
40767 if(doFocus !== false && !this.blockFocus){
40768 this.inputEl().focus();
40772 onViewMove : function(e, t)
40774 this.inKeyMode = false;
40777 select : function(index, scrollIntoView)
40779 this.selectedIndex = index;
40780 this.view.select(index);
40781 if(scrollIntoView !== false){
40782 var el = this.view.getNode(index);
40784 this.list.scrollChildIntoView(el, false);
40789 createList : function()
40791 this.list = Roo.get(document.body).createChild({
40793 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40794 style: 'display:none'
40797 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40800 collapseIf : function(e)
40802 var in_combo = e.within(this.el);
40803 var in_list = e.within(this.list);
40804 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40806 if (in_combo || in_list || is_list) {
40812 onSelect : function(record, index)
40814 if(this.fireEvent('beforeselect', this, record, index) !== false){
40816 this.setFlagClass(record.data.iso2);
40817 this.setDialCode(record.data.dialCode);
40818 this.hasFocus = false;
40820 this.fireEvent('select', this, record, index);
40824 flagEl : function()
40826 var flag = this.el.select('div.flag',true).first();
40833 dialCodeHolderEl : function()
40835 var d = this.el.select('input.dial-code-holder',true).first();
40842 setDialCode : function(v)
40844 this.dialCodeHolder.dom.value = '+'+v;
40847 setFlagClass : function(n)
40849 this.flag.dom.className = 'flag '+n;
40852 getValue : function()
40854 var v = this.inputEl().getValue();
40855 if(this.dialCodeHolder) {
40856 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40861 setValue : function(v)
40863 var d = this.getDialCode(v);
40865 //invalid dial code
40866 if(v.length == 0 || !d || d.length == 0) {
40868 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40869 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40875 this.setFlagClass(this.dialCodeMapping[d].iso2);
40876 this.setDialCode(d);
40877 this.inputEl().dom.value = v.replace('+'+d,'');
40878 this.hiddenEl().dom.value = this.getValue();
40883 getDialCode : function(v)
40887 if (v.length == 0) {
40888 return this.dialCodeHolder.dom.value;
40892 if (v.charAt(0) != "+") {
40895 var numericChars = "";
40896 for (var i = 1; i < v.length; i++) {
40897 var c = v.charAt(i);
40900 if (this.dialCodeMapping[numericChars]) {
40901 dialCode = v.substr(1, i);
40903 if (numericChars.length == 4) {
40913 this.setValue(this.defaultDialCode);
40917 hiddenEl : function()
40919 return this.el.select('input.hidden-tel-input',true).first();
40922 // after setting val
40923 onKeyUp : function(e){
40924 this.setValue(this.getValue());
40927 onKeyPress : function(e){
40928 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40935 * @class Roo.bootstrap.MoneyField
40936 * @extends Roo.bootstrap.ComboBox
40937 * Bootstrap MoneyField class
40940 * Create a new MoneyField.
40941 * @param {Object} config Configuration options
40944 Roo.bootstrap.MoneyField = function(config) {
40946 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40950 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40953 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40955 allowDecimals : true,
40957 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40959 decimalSeparator : ".",
40961 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40963 decimalPrecision : 0,
40965 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40967 allowNegative : true,
40969 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40973 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40975 minValue : Number.NEGATIVE_INFINITY,
40977 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40979 maxValue : Number.MAX_VALUE,
40981 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40983 minText : "The minimum value for this field is {0}",
40985 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40987 maxText : "The maximum value for this field is {0}",
40989 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40990 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40992 nanText : "{0} is not a valid number",
40994 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40998 * @cfg {String} defaults currency of the MoneyField
40999 * value should be in lkey
41001 defaultCurrency : false,
41003 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41005 thousandsDelimiter : false,
41007 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41018 getAutoCreate : function()
41020 var align = this.labelAlign || this.parentLabelAlign();
41032 cls : 'form-control roo-money-amount-input',
41033 autocomplete: 'new-password'
41036 var hiddenInput = {
41040 cls: 'hidden-number-input'
41043 if(this.max_length) {
41044 input.maxlength = this.max_length;
41048 hiddenInput.name = this.name;
41051 if (this.disabled) {
41052 input.disabled = true;
41055 var clg = 12 - this.inputlg;
41056 var cmd = 12 - this.inputmd;
41057 var csm = 12 - this.inputsm;
41058 var cxs = 12 - this.inputxs;
41062 cls : 'row roo-money-field',
41066 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41070 cls: 'roo-select2-container input-group',
41074 cls : 'form-control roo-money-currency-input',
41075 autocomplete: 'new-password',
41077 name : this.currencyName
41081 cls : 'input-group-addon',
41095 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41099 cls: this.hasFeedback ? 'has-feedback' : '',
41110 if (this.fieldLabel.length) {
41113 tooltip: 'This field is required'
41119 cls: 'control-label',
41125 html: this.fieldLabel
41128 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41134 if(this.indicatorpos == 'right') {
41135 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41142 if(align == 'left') {
41150 if(this.labelWidth > 12){
41151 label.style = "width: " + this.labelWidth + 'px';
41153 if(this.labelWidth < 13 && this.labelmd == 0){
41154 this.labelmd = this.labelWidth;
41156 if(this.labellg > 0){
41157 label.cls += ' col-lg-' + this.labellg;
41158 input.cls += ' col-lg-' + (12 - this.labellg);
41160 if(this.labelmd > 0){
41161 label.cls += ' col-md-' + this.labelmd;
41162 container.cls += ' col-md-' + (12 - this.labelmd);
41164 if(this.labelsm > 0){
41165 label.cls += ' col-sm-' + this.labelsm;
41166 container.cls += ' col-sm-' + (12 - this.labelsm);
41168 if(this.labelxs > 0){
41169 label.cls += ' col-xs-' + this.labelxs;
41170 container.cls += ' col-xs-' + (12 - this.labelxs);
41181 var settings = this;
41183 ['xs','sm','md','lg'].map(function(size){
41184 if (settings[size]) {
41185 cfg.cls += ' col-' + size + '-' + settings[size];
41192 initEvents : function()
41194 this.indicator = this.indicatorEl();
41196 this.initCurrencyEvent();
41198 this.initNumberEvent();
41201 initCurrencyEvent : function()
41204 throw "can not find store for combo";
41207 this.store = Roo.factory(this.store, Roo.data);
41208 this.store.parent = this;
41212 this.triggerEl = this.el.select('.input-group-addon', true).first();
41214 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41219 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41220 _this.list.setWidth(lw);
41223 this.list.on('mouseover', this.onViewOver, this);
41224 this.list.on('mousemove', this.onViewMove, this);
41225 this.list.on('scroll', this.onViewScroll, this);
41228 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41231 this.view = new Roo.View(this.list, this.tpl, {
41232 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41235 this.view.on('click', this.onViewClick, this);
41237 this.store.on('beforeload', this.onBeforeLoad, this);
41238 this.store.on('load', this.onLoad, this);
41239 this.store.on('loadexception', this.onLoadException, this);
41241 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41242 "up" : function(e){
41243 this.inKeyMode = true;
41247 "down" : function(e){
41248 if(!this.isExpanded()){
41249 this.onTriggerClick();
41251 this.inKeyMode = true;
41256 "enter" : function(e){
41259 if(this.fireEvent("specialkey", this, e)){
41260 this.onViewClick(false);
41266 "esc" : function(e){
41270 "tab" : function(e){
41273 if(this.fireEvent("specialkey", this, e)){
41274 this.onViewClick(false);
41282 doRelay : function(foo, bar, hname){
41283 if(hname == 'down' || this.scope.isExpanded()){
41284 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41292 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41296 initNumberEvent : function(e)
41298 this.inputEl().on("keydown" , this.fireKey, this);
41299 this.inputEl().on("focus", this.onFocus, this);
41300 this.inputEl().on("blur", this.onBlur, this);
41302 this.inputEl().relayEvent('keyup', this);
41304 if(this.indicator){
41305 this.indicator.addClass('invisible');
41308 this.originalValue = this.getValue();
41310 if(this.validationEvent == 'keyup'){
41311 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41312 this.inputEl().on('keyup', this.filterValidation, this);
41314 else if(this.validationEvent !== false){
41315 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41318 if(this.selectOnFocus){
41319 this.on("focus", this.preFocus, this);
41322 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41323 this.inputEl().on("keypress", this.filterKeys, this);
41325 this.inputEl().relayEvent('keypress', this);
41328 var allowed = "0123456789";
41330 if(this.allowDecimals){
41331 allowed += this.decimalSeparator;
41334 if(this.allowNegative){
41338 if(this.thousandsDelimiter) {
41342 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41344 var keyPress = function(e){
41346 var k = e.getKey();
41348 var c = e.getCharCode();
41351 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41352 allowed.indexOf(String.fromCharCode(c)) === -1
41358 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41362 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41367 this.inputEl().on("keypress", keyPress, this);
41371 onTriggerClick : function(e)
41378 this.loadNext = false;
41380 if(this.isExpanded()){
41385 this.hasFocus = true;
41387 if(this.triggerAction == 'all') {
41388 this.doQuery(this.allQuery, true);
41392 this.doQuery(this.getRawValue());
41395 getCurrency : function()
41397 var v = this.currencyEl().getValue();
41402 restrictHeight : function()
41404 this.list.alignTo(this.currencyEl(), this.listAlign);
41405 this.list.alignTo(this.currencyEl(), this.listAlign);
41408 onViewClick : function(view, doFocus, el, e)
41410 var index = this.view.getSelectedIndexes()[0];
41412 var r = this.store.getAt(index);
41415 this.onSelect(r, index);
41419 onSelect : function(record, index){
41421 if(this.fireEvent('beforeselect', this, record, index) !== false){
41423 this.setFromCurrencyData(index > -1 ? record.data : false);
41427 this.fireEvent('select', this, record, index);
41431 setFromCurrencyData : function(o)
41435 this.lastCurrency = o;
41437 if (this.currencyField) {
41438 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41440 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41443 this.lastSelectionText = currency;
41445 //setting default currency
41446 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41447 this.setCurrency(this.defaultCurrency);
41451 this.setCurrency(currency);
41454 setFromData : function(o)
41458 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41460 this.setFromCurrencyData(c);
41465 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41467 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41470 this.setValue(value);
41474 setCurrency : function(v)
41476 this.currencyValue = v;
41479 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41484 setValue : function(v)
41486 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41492 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41494 this.inputEl().dom.value = (v == '') ? '' :
41495 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41497 if(!this.allowZero && v === '0') {
41498 this.hiddenEl().dom.value = '';
41499 this.inputEl().dom.value = '';
41506 getRawValue : function()
41508 var v = this.inputEl().getValue();
41513 getValue : function()
41515 return this.fixPrecision(this.parseValue(this.getRawValue()));
41518 parseValue : function(value)
41520 if(this.thousandsDelimiter) {
41522 r = new RegExp(",", "g");
41523 value = value.replace(r, "");
41526 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41527 return isNaN(value) ? '' : value;
41531 fixPrecision : function(value)
41533 if(this.thousandsDelimiter) {
41535 r = new RegExp(",", "g");
41536 value = value.replace(r, "");
41539 var nan = isNaN(value);
41541 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41542 return nan ? '' : value;
41544 return parseFloat(value).toFixed(this.decimalPrecision);
41547 decimalPrecisionFcn : function(v)
41549 return Math.floor(v);
41552 validateValue : function(value)
41554 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41558 var num = this.parseValue(value);
41561 this.markInvalid(String.format(this.nanText, value));
41565 if(num < this.minValue){
41566 this.markInvalid(String.format(this.minText, this.minValue));
41570 if(num > this.maxValue){
41571 this.markInvalid(String.format(this.maxText, this.maxValue));
41578 validate : function()
41580 if(this.disabled || this.allowBlank){
41585 var currency = this.getCurrency();
41587 if(this.validateValue(this.getRawValue()) && currency.length){
41592 this.markInvalid();
41596 getName: function()
41601 beforeBlur : function()
41607 var v = this.parseValue(this.getRawValue());
41614 onBlur : function()
41618 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41619 //this.el.removeClass(this.focusClass);
41622 this.hasFocus = false;
41624 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41628 var v = this.getValue();
41630 if(String(v) !== String(this.startValue)){
41631 this.fireEvent('change', this, v, this.startValue);
41634 this.fireEvent("blur", this);
41637 inputEl : function()
41639 return this.el.select('.roo-money-amount-input', true).first();
41642 currencyEl : function()
41644 return this.el.select('.roo-money-currency-input', true).first();
41647 hiddenEl : function()
41649 return this.el.select('input.hidden-number-input',true).first();
41653 * @class Roo.bootstrap.BezierSignature
41654 * @extends Roo.bootstrap.Component
41655 * Bootstrap BezierSignature class
41656 * This script refer to:
41657 * Title: Signature Pad
41659 * Availability: https://github.com/szimek/signature_pad
41662 * Create a new BezierSignature
41663 * @param {Object} config The config object
41666 Roo.bootstrap.BezierSignature = function(config){
41667 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41673 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41680 mouse_btn_down: true,
41683 * @cfg {int} canvas height
41685 canvas_height: '200px',
41688 * @cfg {float|function} Radius of a single dot.
41693 * @cfg {float} Minimum width of a line. Defaults to 0.5.
41698 * @cfg {float} Maximum width of a line. Defaults to 2.5.
41703 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41708 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41713 * @cfg {string} Color used to clear the background. Can be any color format accepted by context.fillStyle. Defaults to "rgba(0,0,0,0)" (transparent black). Use a non-transparent color e.g. "rgb(255,255,255)" (opaque white) if you'd like to save signatures as JPEG images.
41715 bg_color: 'rgba(0, 0, 0, 0)',
41718 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41720 dot_color: 'black',
41723 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41725 velocity_filter_weight: 0.7,
41728 * @cfg {function} Callback when stroke begin.
41733 * @cfg {function} Callback when stroke end.
41737 getAutoCreate : function()
41739 var cls = 'roo-signature column';
41742 cls += ' ' + this.cls;
41752 for(var i = 0; i < col_sizes.length; i++) {
41753 if(this[col_sizes[i]]) {
41754 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41764 cls: 'roo-signature-body',
41768 cls: 'roo-signature-body-canvas',
41769 height: this.canvas_height,
41770 width: this.canvas_width
41777 style: 'display: none'
41785 initEvents: function()
41787 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41789 var canvas = this.canvasEl();
41791 // mouse && touch event swapping...
41792 canvas.dom.style.touchAction = 'none';
41793 canvas.dom.style.msTouchAction = 'none';
41795 this.mouse_btn_down = false;
41796 canvas.on('mousedown', this._handleMouseDown, this);
41797 canvas.on('mousemove', this._handleMouseMove, this);
41798 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41800 if (window.PointerEvent) {
41801 canvas.on('pointerdown', this._handleMouseDown, this);
41802 canvas.on('pointermove', this._handleMouseMove, this);
41803 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41806 if ('ontouchstart' in window) {
41807 canvas.on('touchstart', this._handleTouchStart, this);
41808 canvas.on('touchmove', this._handleTouchMove, this);
41809 canvas.on('touchend', this._handleTouchEnd, this);
41812 Roo.EventManager.onWindowResize(this.resize, this, true);
41814 // file input event
41815 this.fileEl().on('change', this.uploadImage, this);
41822 resize: function(){
41824 var canvas = this.canvasEl().dom;
41825 var ctx = this.canvasElCtx();
41826 var img_data = false;
41828 if(canvas.width > 0) {
41829 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41831 // setting canvas width will clean img data
41834 var style = window.getComputedStyle ?
41835 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41837 var padding_left = parseInt(style.paddingLeft) || 0;
41838 var padding_right = parseInt(style.paddingRight) || 0;
41840 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41843 ctx.putImageData(img_data, 0, 0);
41847 _handleMouseDown: function(e)
41849 if (e.browserEvent.which === 1) {
41850 this.mouse_btn_down = true;
41851 this.strokeBegin(e);
41855 _handleMouseMove: function (e)
41857 if (this.mouse_btn_down) {
41858 this.strokeMoveUpdate(e);
41862 _handleMouseUp: function (e)
41864 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41865 this.mouse_btn_down = false;
41870 _handleTouchStart: function (e) {
41872 e.preventDefault();
41873 if (e.browserEvent.targetTouches.length === 1) {
41874 // var touch = e.browserEvent.changedTouches[0];
41875 // this.strokeBegin(touch);
41877 this.strokeBegin(e); // assume e catching the correct xy...
41881 _handleTouchMove: function (e) {
41882 e.preventDefault();
41883 // var touch = event.targetTouches[0];
41884 // _this._strokeMoveUpdate(touch);
41885 this.strokeMoveUpdate(e);
41888 _handleTouchEnd: function (e) {
41889 var wasCanvasTouched = e.target === this.canvasEl().dom;
41890 if (wasCanvasTouched) {
41891 e.preventDefault();
41892 // var touch = event.changedTouches[0];
41893 // _this._strokeEnd(touch);
41898 reset: function () {
41899 this._lastPoints = [];
41900 this._lastVelocity = 0;
41901 this._lastWidth = (this.min_width + this.max_width) / 2;
41902 this.canvasElCtx().fillStyle = this.dot_color;
41905 strokeMoveUpdate: function(e)
41907 this.strokeUpdate(e);
41909 if (this.throttle) {
41910 this.throttleStroke(this.strokeUpdate, this.throttle);
41913 this.strokeUpdate(e);
41917 strokeBegin: function(e)
41919 var newPointGroup = {
41920 color: this.dot_color,
41924 if (typeof this.onBegin === 'function') {
41928 this.curve_data.push(newPointGroup);
41930 this.strokeUpdate(e);
41933 strokeUpdate: function(e)
41935 var rect = this.canvasEl().dom.getBoundingClientRect();
41936 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41937 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41938 var lastPoints = lastPointGroup.points;
41939 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41940 var isLastPointTooClose = lastPoint
41941 ? point.distanceTo(lastPoint) <= this.min_distance
41943 var color = lastPointGroup.color;
41944 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41945 var curve = this.addPoint(point);
41947 this.drawDot({color: color, point: point});
41950 this.drawCurve({color: color, curve: curve});
41960 strokeEnd: function(e)
41962 this.strokeUpdate(e);
41963 if (typeof this.onEnd === 'function') {
41968 addPoint: function (point) {
41969 var _lastPoints = this._lastPoints;
41970 _lastPoints.push(point);
41971 if (_lastPoints.length > 2) {
41972 if (_lastPoints.length === 3) {
41973 _lastPoints.unshift(_lastPoints[0]);
41975 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
41976 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
41977 _lastPoints.shift();
41983 calculateCurveWidths: function (startPoint, endPoint) {
41984 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
41985 (1 - this.velocity_filter_weight) * this._lastVelocity;
41987 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
41990 start: this._lastWidth
41993 this._lastVelocity = velocity;
41994 this._lastWidth = newWidth;
41998 drawDot: function (_a) {
41999 var color = _a.color, point = _a.point;
42000 var ctx = this.canvasElCtx();
42001 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42003 this.drawCurveSegment(point.x, point.y, width);
42005 ctx.fillStyle = color;
42009 drawCurve: function (_a) {
42010 var color = _a.color, curve = _a.curve;
42011 var ctx = this.canvasElCtx();
42012 var widthDelta = curve.endWidth - curve.startWidth;
42013 var drawSteps = Math.floor(curve.length()) * 2;
42015 ctx.fillStyle = color;
42016 for (var i = 0; i < drawSteps; i += 1) {
42017 var t = i / drawSteps;
42023 var x = uuu * curve.startPoint.x;
42024 x += 3 * uu * t * curve.control1.x;
42025 x += 3 * u * tt * curve.control2.x;
42026 x += ttt * curve.endPoint.x;
42027 var y = uuu * curve.startPoint.y;
42028 y += 3 * uu * t * curve.control1.y;
42029 y += 3 * u * tt * curve.control2.y;
42030 y += ttt * curve.endPoint.y;
42031 var width = curve.startWidth + ttt * widthDelta;
42032 this.drawCurveSegment(x, y, width);
42038 drawCurveSegment: function (x, y, width) {
42039 var ctx = this.canvasElCtx();
42041 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42042 this.is_empty = false;
42047 var ctx = this.canvasElCtx();
42048 var canvas = this.canvasEl().dom;
42049 ctx.fillStyle = this.bg_color;
42050 ctx.clearRect(0, 0, canvas.width, canvas.height);
42051 ctx.fillRect(0, 0, canvas.width, canvas.height);
42052 this.curve_data = [];
42054 this.is_empty = true;
42059 return this.el.select('input',true).first();
42062 canvasEl: function()
42064 return this.el.select('canvas',true).first();
42067 canvasElCtx: function()
42069 return this.el.select('canvas',true).first().dom.getContext('2d');
42072 getImage: function(type)
42074 if(this.is_empty) {
42079 return this.canvasEl().dom.toDataURL('image/'+type, 1);
42082 drawFromImage: function(img_src)
42084 var img = new Image();
42086 img.onload = function(){
42087 this.canvasElCtx().drawImage(img, 0, 0);
42092 this.is_empty = false;
42095 selectImage: function()
42097 this.fileEl().dom.click();
42100 uploadImage: function(e)
42102 var reader = new FileReader();
42104 reader.onload = function(e){
42105 var img = new Image();
42106 img.onload = function(){
42108 this.canvasElCtx().drawImage(img, 0, 0);
42110 img.src = e.target.result;
42113 reader.readAsDataURL(e.target.files[0]);
42116 // Bezier Point Constructor
42117 Point: (function () {
42118 function Point(x, y, time) {
42121 this.time = time || Date.now();
42123 Point.prototype.distanceTo = function (start) {
42124 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42126 Point.prototype.equals = function (other) {
42127 return this.x === other.x && this.y === other.y && this.time === other.time;
42129 Point.prototype.velocityFrom = function (start) {
42130 return this.time !== start.time
42131 ? this.distanceTo(start) / (this.time - start.time)
42138 // Bezier Constructor
42139 Bezier: (function () {
42140 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42141 this.startPoint = startPoint;
42142 this.control2 = control2;
42143 this.control1 = control1;
42144 this.endPoint = endPoint;
42145 this.startWidth = startWidth;
42146 this.endWidth = endWidth;
42148 Bezier.fromPoints = function (points, widths, scope) {
42149 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42150 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42151 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42153 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42154 var dx1 = s1.x - s2.x;
42155 var dy1 = s1.y - s2.y;
42156 var dx2 = s2.x - s3.x;
42157 var dy2 = s2.y - s3.y;
42158 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42159 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42160 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42161 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42162 var dxm = m1.x - m2.x;
42163 var dym = m1.y - m2.y;
42164 var k = l2 / (l1 + l2);
42165 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42166 var tx = s2.x - cm.x;
42167 var ty = s2.y - cm.y;
42169 c1: new scope.Point(m1.x + tx, m1.y + ty),
42170 c2: new scope.Point(m2.x + tx, m2.y + ty)
42173 Bezier.prototype.length = function () {
42178 for (var i = 0; i <= steps; i += 1) {
42180 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42181 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42183 var xdiff = cx - px;
42184 var ydiff = cy - py;
42185 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42192 Bezier.prototype.point = function (t, start, c1, c2, end) {
42193 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42194 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42195 + (3.0 * c2 * (1.0 - t) * t * t)
42196 + (end * t * t * t);
42201 throttleStroke: function(fn, wait) {
42202 if (wait === void 0) { wait = 250; }
42204 var timeout = null;
42208 var later = function () {
42209 previous = Date.now();
42211 result = fn.apply(storedContext, storedArgs);
42213 storedContext = null;
42217 return function wrapper() {
42219 for (var _i = 0; _i < arguments.length; _i++) {
42220 args[_i] = arguments[_i];
42222 var now = Date.now();
42223 var remaining = wait - (now - previous);
42224 storedContext = this;
42226 if (remaining <= 0 || remaining > wait) {
42228 clearTimeout(timeout);
42232 result = fn.apply(storedContext, storedArgs);
42234 storedContext = null;
42238 else if (!timeout) {
42239 timeout = window.setTimeout(later, remaining);