2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
366 * Fetch the element to display the tooltip on.
367 * @return {Roo.Element} defaults to this.el
369 tooltipEl : function()
374 addxtype : function(tree,cntr)
378 cn = Roo.factory(tree);
379 //Roo.log(['addxtype', cn]);
381 cn.parentType = this.xtype; //??
382 cn.parentId = this.id;
384 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385 if (typeof(cn.container_method) == 'string') {
386 cntr = cn.container_method;
390 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
392 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
394 var build_from_html = Roo.XComponent.build_from_html;
396 var is_body = (tree.xtype == 'Body') ;
398 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
400 var self_cntr_el = Roo.get(this[cntr](false));
402 // do not try and build conditional elements
403 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
407 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409 return this.addxtypeChild(tree,cntr, is_body);
412 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
415 return this.addxtypeChild(Roo.apply({}, tree),cntr);
418 Roo.log('skipping render');
424 if (!build_from_html) {
428 // this i think handles overlaying multiple children of the same type
429 // with the sam eelement.. - which might be buggy..
431 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
437 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
441 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
448 addxtypeChild : function (tree, cntr, is_body)
450 Roo.debug && Roo.log('addxtypeChild:' + cntr);
452 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
455 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456 (typeof(tree['flexy:foreach']) != 'undefined');
460 skip_children = false;
461 // render the element if it's not BODY.
464 // if parent was disabled, then do not try and create the children..
465 if(!this[cntr](true)){
470 cn = Roo.factory(tree);
472 cn.parentType = this.xtype; //??
473 cn.parentId = this.id;
475 var build_from_html = Roo.XComponent.build_from_html;
478 // does the container contain child eleemnts with 'xtype' attributes.
479 // that match this xtype..
480 // note - when we render we create these as well..
481 // so we should check to see if body has xtype set.
482 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
484 var self_cntr_el = Roo.get(this[cntr](false));
485 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
487 //Roo.log(Roo.XComponent.build_from_html);
488 //Roo.log("got echild:");
491 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492 // and are not displayed -this causes this to use up the wrong element when matching.
493 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
496 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
503 //echild.dom.removeAttribute('xtype');
505 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506 Roo.debug && Roo.log(self_cntr_el);
507 Roo.debug && Roo.log(echild);
508 Roo.debug && Roo.log(cn);
514 // if object has flexy:if - then it may or may not be rendered.
515 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
516 // skip a flexy if element.
517 Roo.debug && Roo.log('skipping render');
518 Roo.debug && Roo.log(tree);
520 Roo.debug && Roo.log('skipping all children');
521 skip_children = true;
526 // actually if flexy:foreach is found, we really want to create
527 // multiple copies here...
529 //Roo.log(this[cntr]());
530 // some elements do not have render methods.. like the layouts...
532 if(this[cntr](true) === false){
537 cn.render && cn.render(this[cntr](true));
540 // then add the element..
547 if (typeof (tree.menu) != 'undefined') {
548 tree.menu.parentType = cn.xtype;
549 tree.menu.triggerEl = cn.el;
550 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
554 if (!tree.items || !tree.items.length) {
556 //Roo.log(["no children", this]);
561 var items = tree.items;
564 //Roo.log(items.length);
566 if (!skip_children) {
567 for(var i =0;i < items.length;i++) {
568 // Roo.log(['add child', items[i]]);
569 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
575 //Roo.log("fire childrenrendered");
577 cn.fireEvent('childrenrendered', this);
583 * Set the element that will be used to show or hide
585 setVisibilityEl : function(el)
587 this.visibilityEl = el;
591 * Get the element that will be used to show or hide
593 getVisibilityEl : function()
595 if (typeof(this.visibilityEl) == 'object') {
596 return this.visibilityEl;
599 if (typeof(this.visibilityEl) == 'string') {
600 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
607 * Show a component - removes 'hidden' class
611 if(!this.getVisibilityEl()){
615 this.getVisibilityEl().removeClass(['hidden','d-none']);
617 this.fireEvent('show', this);
622 * Hide a component - adds 'hidden' class
626 if(!this.getVisibilityEl()){
630 this.getVisibilityEl().addClass(['hidden','d-none']);
632 this.fireEvent('hide', this);
645 * @class Roo.bootstrap.Element
646 * @extends Roo.bootstrap.Component
647 * Bootstrap Element class
648 * @cfg {String} html contents of the element
649 * @cfg {String} tag tag of the element
650 * @cfg {String} cls class of the element
651 * @cfg {Boolean} preventDefault (true|false) default false
652 * @cfg {Boolean} clickable (true|false) default false
655 * Create a new Element
656 * @param {Object} config The config object
659 Roo.bootstrap.Element = function(config){
660 Roo.bootstrap.Element.superclass.constructor.call(this, config);
666 * When a element is chick
667 * @param {Roo.bootstrap.Element} this
668 * @param {Roo.EventObject} e
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
679 preventDefault: false,
682 getAutoCreate : function(){
686 // cls: this.cls, double assign in parent class Component.js :: onRender
693 initEvents: function()
695 Roo.bootstrap.Element.superclass.initEvents.call(this);
698 this.el.on('click', this.onClick, this);
703 onClick : function(e)
705 if(this.preventDefault){
709 this.fireEvent('click', this, e);
712 getValue : function()
714 return this.el.dom.innerHTML;
717 setValue : function(value)
719 this.el.dom.innerHTML = value;
734 * @class Roo.bootstrap.DropTarget
735 * @extends Roo.bootstrap.Element
736 * Bootstrap DropTarget class
738 * @cfg {string} name dropable name
741 * Create a new Dropable Area
742 * @param {Object} config The config object
745 Roo.bootstrap.DropTarget = function(config){
746 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
752 * When a element is chick
753 * @param {Roo.bootstrap.Element} this
754 * @param {Roo.EventObject} e
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
763 getAutoCreate : function(){
768 initEvents: function()
770 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
774 drop : this.dragDrop.createDelegate(this),
775 enter : this.dragEnter.createDelegate(this),
776 out : this.dragOut.createDelegate(this),
777 over : this.dragOver.createDelegate(this)
781 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
784 dragDrop : function(source,e,data)
786 // user has to decide how to impliment this.
789 //this.fireEvent('drop', this, source, e ,data);
793 dragEnter : function(n, dd, e, data)
795 // probably want to resize the element to match the dropped element..
797 this.originalSize = this.el.getSize();
798 this.el.setSize( n.el.getSize());
799 this.dropZone.DDM.refreshCache(this.name);
800 Roo.log([n, dd, e, data]);
803 dragOut : function(value)
805 // resize back to normal
807 this.el.setSize(this.originalSize);
808 this.dropZone.resetConstraints();
811 dragOver : function()
828 * @class Roo.bootstrap.Body
829 * @extends Roo.bootstrap.Component
830 * Bootstrap Body class
834 * @param {Object} config The config object
837 Roo.bootstrap.Body = function(config){
839 config = config || {};
841 Roo.bootstrap.Body.superclass.constructor.call(this, config);
842 this.el = Roo.get(config.el ? config.el : document.body );
843 if (this.cls && this.cls.length) {
844 Roo.get(document.body).addClass(this.cls);
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
850 is_body : true,// just to make sure it's constructed?
855 onRender : function(ct, position)
857 /* Roo.log("Roo.bootstrap.Body - onRender");
858 if (this.cls && this.cls.length) {
859 Roo.get(document.body).addClass(this.cls);
878 * @class Roo.bootstrap.ButtonGroup
879 * @extends Roo.bootstrap.Component
880 * Bootstrap ButtonGroup class
881 * @cfg {String} size lg | sm | xs (default empty normal)
882 * @cfg {String} align vertical | justified (default none)
883 * @cfg {String} direction up | down (default down)
884 * @cfg {Boolean} toolbar false | true
885 * @cfg {Boolean} btn true | false
890 * @param {Object} config The config object
893 Roo.bootstrap.ButtonGroup = function(config){
894 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
905 getAutoCreate : function(){
911 cfg.html = this.html || cfg.html;
922 if (['vertical','justified'].indexOf(this.align)!==-1) {
923 cfg.cls = 'btn-group-' + this.align;
925 if (this.align == 'justified') {
926 console.log(this.items);
930 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931 cfg.cls += ' btn-group-' + this.size;
934 if (this.direction == 'up') {
935 cfg.cls += ' dropup' ;
941 * Add a button to the group (similar to NavItem API.)
943 addItem : function(cfg)
945 var cn = new Roo.bootstrap.Button(cfg);
947 cn.parentId = this.id;
948 cn.onRender(this.el, null);
962 * @class Roo.bootstrap.Button
963 * @extends Roo.bootstrap.Component
964 * Bootstrap Button class
965 * @cfg {String} html The button content
966 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969 * @cfg {String} size (lg|sm|xs)
970 * @cfg {String} tag (a|input|submit)
971 * @cfg {String} href empty or href
972 * @cfg {Boolean} disabled default false;
973 * @cfg {Boolean} isClose default false;
974 * @cfg {String} glyphicon depricated - use fa
975 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976 * @cfg {String} badge text for badge
977 * @cfg {String} theme (default|glow)
978 * @cfg {Boolean} inverse dark themed version
979 * @cfg {Boolean} toggle is it a slidy toggle button
980 * @cfg {Boolean} pressed default null - if the button ahs active state
981 * @cfg {String} ontext text for on slidy toggle state
982 * @cfg {String} offtext text for off slidy toggle state
983 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
984 * @cfg {Boolean} removeClass remove the standard class..
985 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
986 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
989 * Create a new button
990 * @param {Object} config The config object
994 Roo.bootstrap.Button = function(config){
995 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1001 * When a button is pressed
1002 * @param {Roo.bootstrap.Button} btn
1003 * @param {Roo.EventObject} e
1008 * When a button is double clicked
1009 * @param {Roo.bootstrap.Button} btn
1010 * @param {Roo.EventObject} e
1015 * After the button has been toggles
1016 * @param {Roo.bootstrap.Button} btn
1017 * @param {Roo.EventObject} e
1018 * @param {boolean} pressed (also available as button.pressed)
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1045 preventDefault: true,
1054 getAutoCreate : function(){
1062 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064 this.tag = 'button';
1068 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1070 if (this.toggle == true) {
1073 cls: 'slider-frame roo-button',
1077 'data-on-text':'ON',
1078 'data-off-text':'OFF',
1079 cls: 'slider-button',
1084 // why are we validating the weights?
1085 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086 cfg.cls += ' ' + this.weight;
1093 cfg.cls += ' close';
1095 cfg["aria-hidden"] = true;
1097 cfg.html = "×";
1103 if (this.theme==='default') {
1104 cfg.cls = 'btn roo-button';
1106 //if (this.parentType != 'Navbar') {
1107 this.weight = this.weight.length ? this.weight : 'default';
1109 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1111 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113 cfg.cls += ' btn-' + outline + weight;
1114 if (this.weight == 'default') {
1116 cfg.cls += ' btn-' + this.weight;
1119 } else if (this.theme==='glow') {
1122 cfg.cls = 'btn-glow roo-button';
1124 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1126 cfg.cls += ' ' + this.weight;
1132 this.cls += ' inverse';
1136 if (this.active || this.pressed === true) {
1137 cfg.cls += ' active';
1140 if (this.disabled) {
1141 cfg.disabled = 'disabled';
1145 Roo.log('changing to ul' );
1147 this.glyphicon = 'caret';
1148 if (Roo.bootstrap.version == 4) {
1149 this.fa = 'caret-down';
1154 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1156 //gsRoo.log(this.parentType);
1157 if (this.parentType === 'Navbar' && !this.parent().bar) {
1158 Roo.log('changing to li?');
1167 href : this.href || '#'
1170 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1171 cfg.cls += ' dropdown';
1178 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1180 if (this.glyphicon) {
1181 cfg.html = ' ' + cfg.html;
1186 cls: 'glyphicon glyphicon-' + this.glyphicon
1191 cfg.html = ' ' + cfg.html;
1196 cls: 'fa fas fa-' + this.fa
1206 // cfg.cls='btn roo-button';
1210 var value = cfg.html;
1215 cls: 'glyphicon glyphicon-' + this.glyphicon,
1222 cls: 'fa fas fa-' + this.fa,
1227 var bw = this.badge_weight.length ? this.badge_weight :
1228 (this.weight.length ? this.weight : 'secondary');
1229 bw = bw == 'default' ? 'secondary' : bw;
1235 cls: 'badge badge-' + bw,
1244 cfg.cls += ' dropdown';
1245 cfg.html = typeof(cfg.html) != 'undefined' ?
1246 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1249 if (cfg.tag !== 'a' && this.href !== '') {
1250 throw "Tag must be a to set href.";
1251 } else if (this.href.length > 0) {
1252 cfg.href = this.href;
1255 if(this.removeClass){
1260 cfg.target = this.target;
1265 initEvents: function() {
1266 // Roo.log('init events?');
1267 // Roo.log(this.el.dom);
1270 if (typeof (this.menu) != 'undefined') {
1271 this.menu.parentType = this.xtype;
1272 this.menu.triggerEl = this.el;
1273 this.addxtype(Roo.apply({}, this.menu));
1277 if (this.el.hasClass('roo-button')) {
1278 this.el.on('click', this.onClick, this);
1279 this.el.on('dblclick', this.onDblClick, this);
1281 this.el.select('.roo-button').on('click', this.onClick, this);
1282 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1286 if(this.removeClass){
1287 this.el.on('click', this.onClick, this);
1290 if (this.group === true) {
1291 if (this.pressed === false || this.pressed === true) {
1294 this.pressed = false;
1295 this.setActive(this.pressed);
1300 this.el.enableDisplayMode();
1303 onClick : function(e)
1305 if (this.disabled) {
1309 Roo.log('button on click ');
1310 if(this.preventDefault){
1319 this.setActive(true);
1320 var pi = this.parent().items;
1321 for (var i = 0;i < pi.length;i++) {
1322 if (this == pi[i]) {
1325 if (pi[i].el.hasClass('roo-button')) {
1326 pi[i].setActive(false);
1329 this.fireEvent('click', this, e);
1333 if (this.pressed === true || this.pressed === false) {
1334 this.toggleActive(e);
1338 this.fireEvent('click', this, e);
1340 onDblClick: function(e)
1342 if (this.disabled) {
1345 if(this.preventDefault){
1348 this.fireEvent('dblclick', this, e);
1351 * Enables this button
1355 this.disabled = false;
1356 this.el.removeClass('disabled');
1360 * Disable this button
1362 disable : function()
1364 this.disabled = true;
1365 this.el.addClass('disabled');
1368 * sets the active state on/off,
1369 * @param {Boolean} state (optional) Force a particular state
1371 setActive : function(v) {
1373 this.el[v ? 'addClass' : 'removeClass']('active');
1377 * toggles the current active state
1379 toggleActive : function(e)
1381 this.setActive(!this.pressed); // this modifies pressed...
1382 this.fireEvent('toggle', this, e, this.pressed);
1385 * get the current active state
1386 * @return {boolean} true if it's active
1388 isActive : function()
1390 return this.el.hasClass('active');
1393 * set the text of the first selected button
1395 setText : function(str)
1397 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1400 * get the text of the first selected button
1402 getText : function()
1404 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1407 setWeight : function(str)
1409 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1412 var outline = this.outline ? 'outline-' : '';
1413 if (str == 'default') {
1414 this.el.addClass('btn-default btn-outline-secondary');
1417 this.el.addClass('btn-' + outline + str);
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1424 Roo.bootstrap.Button.weights = [
1444 * @class Roo.bootstrap.Column
1445 * @extends Roo.bootstrap.Component
1446 * Bootstrap Column class
1447 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1457 * @cfg {Boolean} hidden (true|false) hide the element
1458 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459 * @cfg {String} fa (ban|check|...) font awesome icon
1460 * @cfg {Number} fasize (1|2|....) font awsome size
1462 * @cfg {String} icon (info-sign|check|...) glyphicon name
1464 * @cfg {String} html content of column.
1467 * Create a new Column
1468 * @param {Object} config The config object
1471 Roo.bootstrap.Column = function(config){
1472 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1493 getAutoCreate : function(){
1494 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1502 var sizes = ['xs','sm','md','lg'];
1503 sizes.map(function(size ,ix){
1504 //Roo.log( size + ':' + settings[size]);
1506 if (settings[size+'off'] !== false) {
1507 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1510 if (settings[size] === false) {
1514 if (!settings[size]) { // 0 = hidden
1515 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1517 for (var i = ix; i > -1; i--) {
1518 cfg.cls += ' d-' + sizes[i] + '-none';
1524 cfg.cls += ' col-' + size + '-' + settings[size] + (
1525 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1531 cfg.cls += ' hidden';
1534 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535 cfg.cls +=' alert alert-' + this.alert;
1539 if (this.html.length) {
1540 cfg.html = this.html;
1544 if (this.fasize > 1) {
1545 fasize = ' fa-' + this.fasize + 'x';
1547 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1552 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1571 * @class Roo.bootstrap.Container
1572 * @extends Roo.bootstrap.Component
1573 * Bootstrap Container class
1574 * @cfg {Boolean} jumbotron is it a jumbotron element
1575 * @cfg {String} html content of element
1576 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1578 * @cfg {String} header content of header (for panel)
1579 * @cfg {String} footer content of footer (for panel)
1580 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581 * @cfg {String} tag (header|aside|section) type of HTML tag.
1582 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583 * @cfg {String} fa font awesome icon
1584 * @cfg {String} icon (info-sign|check|...) glyphicon name
1585 * @cfg {Boolean} hidden (true|false) hide the element
1586 * @cfg {Boolean} expandable (true|false) default false
1587 * @cfg {Boolean} expanded (true|false) default true
1588 * @cfg {String} rheader contet on the right of header
1589 * @cfg {Boolean} clickable (true|false) default false
1593 * Create a new Container
1594 * @param {Object} config The config object
1597 Roo.bootstrap.Container = function(config){
1598 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1604 * After the panel has been expand
1606 * @param {Roo.bootstrap.Container} this
1611 * After the panel has been collapsed
1613 * @param {Roo.bootstrap.Container} this
1618 * When a element is chick
1619 * @param {Roo.bootstrap.Container} this
1620 * @param {Roo.EventObject} e
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1644 getChildContainer : function() {
1650 if (this.panel.length) {
1651 return this.el.select('.panel-body',true).first();
1658 getAutoCreate : function(){
1661 tag : this.tag || 'div',
1665 if (this.jumbotron) {
1666 cfg.cls = 'jumbotron';
1671 // - this is applied by the parent..
1673 // cfg.cls = this.cls + '';
1676 if (this.sticky.length) {
1678 var bd = Roo.get(document.body);
1679 if (!bd.hasClass('bootstrap-sticky')) {
1680 bd.addClass('bootstrap-sticky');
1681 Roo.select('html',true).setStyle('height', '100%');
1684 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1688 if (this.well.length) {
1689 switch (this.well) {
1692 cfg.cls +=' well well-' +this.well;
1701 cfg.cls += ' hidden';
1705 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706 cfg.cls +=' alert alert-' + this.alert;
1711 if (this.panel.length) {
1712 cfg.cls += ' panel panel-' + this.panel;
1714 if (this.header.length) {
1718 if(this.expandable){
1720 cfg.cls = cfg.cls + ' expandable';
1724 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1732 cls : 'panel-title',
1733 html : (this.expandable ? ' ' : '') + this.header
1737 cls: 'panel-header-right',
1743 cls : 'panel-heading',
1744 style : this.expandable ? 'cursor: pointer' : '',
1752 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1757 if (this.footer.length) {
1759 cls : 'panel-footer',
1768 body.html = this.html || cfg.html;
1769 // prefix with the icons..
1771 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1774 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1779 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780 cfg.cls = 'container';
1786 initEvents: function()
1788 if(this.expandable){
1789 var headerEl = this.headerEl();
1792 headerEl.on('click', this.onToggleClick, this);
1797 this.el.on('click', this.onClick, this);
1802 onToggleClick : function()
1804 var headerEl = this.headerEl();
1820 if(this.fireEvent('expand', this)) {
1822 this.expanded = true;
1824 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1826 this.el.select('.panel-body',true).first().removeClass('hide');
1828 var toggleEl = this.toggleEl();
1834 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1839 collapse : function()
1841 if(this.fireEvent('collapse', this)) {
1843 this.expanded = false;
1845 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846 this.el.select('.panel-body',true).first().addClass('hide');
1848 var toggleEl = this.toggleEl();
1854 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1858 toggleEl : function()
1860 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1864 return this.el.select('.panel-heading .fa',true).first();
1867 headerEl : function()
1869 if(!this.el || !this.panel.length || !this.header.length){
1873 return this.el.select('.panel-heading',true).first()
1878 if(!this.el || !this.panel.length){
1882 return this.el.select('.panel-body',true).first()
1885 titleEl : function()
1887 if(!this.el || !this.panel.length || !this.header.length){
1891 return this.el.select('.panel-title',true).first();
1894 setTitle : function(v)
1896 var titleEl = this.titleEl();
1902 titleEl.dom.innerHTML = v;
1905 getTitle : function()
1908 var titleEl = this.titleEl();
1914 return titleEl.dom.innerHTML;
1917 setRightTitle : function(v)
1919 var t = this.el.select('.panel-header-right',true).first();
1925 t.dom.innerHTML = v;
1928 onClick : function(e)
1932 this.fireEvent('click', this, e);
1939 * This is BS4's Card element.. - similar to our containers probably..
1943 * @class Roo.bootstrap.Card
1944 * @extends Roo.bootstrap.Component
1945 * Bootstrap Card class
1948 * possible... may not be implemented..
1949 * @cfg {String} header_image src url of image.
1950 * @cfg {String|Object} header
1951 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1954 * @cfg {String} title
1955 * @cfg {String} subtitle
1956 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957 * @cfg {String} footer
1959 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1961 * @cfg {String} margin (0|1|2|3|4|5|auto)
1962 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1969 * @cfg {String} padding (0|1|2|3|4|5)
1970 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972 * @cfg {String} padding_left (0|1|2|3|4|5)
1973 * @cfg {String} padding_right (0|1|2|3|4|5)
1974 * @cfg {String} padding_x (0|1|2|3|4|5)
1975 * @cfg {String} padding_y (0|1|2|3|4|5)
1977 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1983 * @config {Boolean} dragable if this card can be dragged.
1984 * @config {String} drag_group group for drag
1985 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
1986 * @config {String} drop_group group for drag
1988 * @config {Boolean} collapsable can the body be collapsed.
1989 * @config {Boolean} collapsed is the body collapsed when rendered...
1990 * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991 * @config {Boolean} rotated is the body rotated when rendered...
1994 * Create a new Container
1995 * @param {Object} config The config object
1998 Roo.bootstrap.Card = function(config){
1999 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2005 * When a element a card is dropped
2006 * @param {Roo.bootstrap.Card} this
2009 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010 * @param {String} position 'above' or 'below'
2011 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2017 * When a element a card is rotate
2018 * @param {Roo.bootstrap.Element} this
2019 * @param {Roo.Element} n the node being dropped?
2020 * @param {Boolean} rotate status
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2033 margin: '', /// may be better in component?
2063 collapsable : false,
2072 childContainer : false,
2073 dropEl : false, /// the dom placeholde element that indicates drop location.
2074 containerEl: false, // body container
2075 bodyEl: false, // card-body
2076 headerContainerEl : false, //
2079 layoutCls : function()
2083 Roo.log(this.margin_bottom.length);
2084 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2087 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2090 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2095 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2101 // more generic support?
2109 // Roo.log("Call onRender: " + this.xtype);
2110 /* We are looking at something like this.
2112 <img src="..." class="card-img-top" alt="...">
2113 <div class="card-body">
2114 <h5 class="card-title">Card title</h5>
2115 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2117 >> this bit is really the body...
2118 <div> << we will ad dthis in hopefully it will not break shit.
2120 ** card text does not actually have any styling...
2122 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2125 <a href="#" class="card-link">Card link</a>
2128 <div class="card-footer">
2129 <small class="text-muted">Last updated 3 mins ago</small>
2133 getAutoCreate : function(){
2141 if (this.weight.length && this.weight != 'light') {
2142 cfg.cls += ' text-white';
2144 cfg.cls += ' text-dark'; // need as it's nested..
2146 if (this.weight.length) {
2147 cfg.cls += ' bg-' + this.weight;
2150 cfg.cls += ' ' + this.layoutCls();
2153 var hdr_ctr = false;
2154 if (this.header.length) {
2156 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2165 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2171 if (this.collapsable) {
2174 cls : 'd-block user-select-none',
2178 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2183 hdr.cn.push(hdr_ctr);
2188 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2193 if (this.header_image.length) {
2196 cls : 'card-img-top',
2197 src: this.header_image // escape?
2202 cls : 'card-img-top d-none'
2208 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2212 if (this.collapsable || this.rotateable) {
2215 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2222 if (this.title.length) {
2226 src: this.title // escape?
2230 if (this.subtitle.length) {
2234 src: this.subtitle // escape?
2240 cls : 'roo-card-body-ctr'
2243 if (this.html.length) {
2249 // fixme ? handle objects?
2251 if (this.footer.length) {
2254 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2259 cfg.cn.push({cls : 'card-footer d-none'});
2268 getCardHeader : function()
2270 var ret = this.el.select('.card-header',true).first();
2271 if (ret.hasClass('d-none')) {
2272 ret.removeClass('d-none');
2277 getCardFooter : function()
2279 var ret = this.el.select('.card-footer',true).first();
2280 if (ret.hasClass('d-none')) {
2281 ret.removeClass('d-none');
2286 getCardImageTop : function()
2288 var ret = this.el.select('.card-img-top',true).first();
2289 if (ret.hasClass('d-none')) {
2290 ret.removeClass('d-none');
2296 getChildContainer : function()
2302 return this.el.select('.roo-card-body-ctr',true).first();
2305 initEvents: function()
2307 this.bodyEl = this.el.select('.card-body',true).first();
2308 this.containerEl = this.getChildContainer();
2310 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311 containerScroll: true,
2312 ddGroup: this.drag_group || 'default_card_drag_group'
2314 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2316 if (this.dropable) {
2317 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318 containerScroll: true,
2319 ddGroup: this.drop_group || 'default_card_drag_group'
2321 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2328 if (this.collapsable) {
2329 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2331 if (this.rotateable) {
2332 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2334 this.collapsableEl = this.el.select('.roo-collapsable').first();
2336 this.footerEl = this.el.select('.card-footer').first();
2337 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338 this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339 this.headerEl = this.el.select('.card-header',true).first();
2342 this.el.addClass('roo-card-rotated');
2343 this.fireEvent('rotate', this, true);
2347 getDragData : function(e)
2349 var target = this.getEl();
2351 //this.handleSelection(e);
2356 nodes: this.getEl(),
2361 dragData.ddel = target.dom ; // the div element
2362 Roo.log(target.getWidth( ));
2363 dragData.ddel.style.width = target.getWidth() + 'px';
2370 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2371 * whole Element becomes the target, and this causes the drop gesture to append.
2373 getTargetFromEvent : function(e, dragged_card_el)
2375 var target = e.getTarget();
2376 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377 target = target.parentNode;
2388 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389 // see if target is one of the 'cards'...
2392 //Roo.log(this.items.length);
2395 var last_card_n = 0;
2397 for (var i = 0;i< this.items.length;i++) {
2399 if (!this.items[i].el.hasClass('card')) {
2402 pos = this.getDropPoint(e, this.items[i].el.dom);
2404 cards_len = ret.cards.length;
2405 //Roo.log(this.items[i].el.dom.id);
2406 ret.cards.push(this.items[i]);
2408 if (ret.card_n < 0 && pos == 'above') {
2409 ret.position = cards_len > 0 ? 'below' : pos;
2410 ret.items_n = i > 0 ? i - 1 : 0;
2411 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2412 ret.card = ret.cards[ret.card_n];
2415 if (!ret.cards.length) {
2417 ret.position = 'below';
2421 // could not find a card.. stick it at the end..
2422 if (ret.card_n < 0) {
2423 ret.card_n = last_card_n;
2424 ret.card = ret.cards[last_card_n];
2425 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426 ret.position = 'below';
2429 if (this.items[ret.items_n].el == dragged_card_el) {
2433 if (ret.position == 'below') {
2434 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2436 if (card_after && card_after.el == dragged_card_el) {
2443 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2445 if (card_before && card_before.el == dragged_card_el) {
2452 onNodeEnter : function(n, dd, e, data){
2455 onNodeOver : function(n, dd, e, data)
2458 var target_info = this.getTargetFromEvent(e,data.source.el);
2459 if (target_info === false) {
2460 this.dropPlaceHolder('hide');
2463 Roo.log(['getTargetFromEvent', target_info ]);
2466 this.dropPlaceHolder('show', target_info,data);
2470 onNodeOut : function(n, dd, e, data){
2471 this.dropPlaceHolder('hide');
2474 onNodeDrop : function(n, dd, e, data)
2477 // call drop - return false if
2479 // this could actually fail - if the Network drops..
2480 // we will ignore this at present..- client should probably reload
2481 // the whole set of cards if stuff like that fails.
2484 var info = this.getTargetFromEvent(e,data.source.el);
2485 if (info === false) {
2488 this.dropPlaceHolder('hide');
2494 this.acceptCard(data.source, info.position, info.card, info.items_n);
2498 firstChildCard : function()
2500 for (var i = 0;i< this.items.length;i++) {
2502 if (!this.items[i].el.hasClass('card')) {
2505 return this.items[i];
2507 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2512 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2514 acceptCard : function(move_card, position, next_to_card )
2516 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2520 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2522 move_card.parent().removeCard(move_card);
2525 var dom = move_card.el.dom;
2526 dom.style.width = ''; // clear with - which is set by drag.
2528 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2529 var cardel = next_to_card.el.dom;
2531 if (position == 'above' ) {
2532 cardel.parentNode.insertBefore(dom, cardel);
2533 } else if (cardel.nextSibling) {
2534 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2536 cardel.parentNode.append(dom);
2539 // card container???
2540 this.containerEl.dom.append(dom);
2543 //FIXME HANDLE card = true
2545 // add this to the correct place in items.
2547 // remove Card from items.
2550 if (this.items.length) {
2552 //Roo.log([info.items_n, info.position, this.items.length]);
2553 for (var i =0; i < this.items.length; i++) {
2554 if (i == to_items_n && position == 'above') {
2555 nitems.push(move_card);
2557 nitems.push(this.items[i]);
2558 if (i == to_items_n && position == 'below') {
2559 nitems.push(move_card);
2562 this.items = nitems;
2563 Roo.log(this.items);
2565 this.items.push(move_card);
2568 move_card.parentId = this.id;
2574 removeCard : function(c)
2576 this.items = this.items.filter(function(e) { return e != c });
2579 dom.parentNode.removeChild(dom);
2580 dom.style.width = ''; // clear with - which is set by drag.
2585 /** Decide whether to drop above or below a View node. */
2586 getDropPoint : function(e, n, dd)
2591 if (n == this.containerEl.dom) {
2594 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2595 var c = t + (b - t) / 2;
2596 var y = Roo.lib.Event.getPageY(e);
2603 onToggleCollapse : function(e)
2605 if (this.collapsed) {
2606 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2607 this.collapsableEl.addClass('show');
2608 this.collapsed = false;
2611 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2612 this.collapsableEl.removeClass('show');
2613 this.collapsed = true;
2618 onToggleRotate : function(e)
2620 this.collapsableEl.removeClass('show');
2621 this.footerEl.removeClass('d-none');
2622 this.el.removeClass('roo-card-rotated');
2623 this.el.removeClass('d-none');
2626 this.collapsableEl.addClass('show');
2627 this.rotated = false;
2628 this.fireEvent('rotate', this, this.rotated);
2631 this.el.addClass('roo-card-rotated');
2632 this.footerEl.addClass('d-none');
2633 this.el.select('.roo-collapsable').removeClass('show');
2635 this.rotated = true;
2636 this.fireEvent('rotate', this, this.rotated);
2640 dropPlaceHolder: function (action, info, data)
2642 if (this.dropEl === false) {
2643 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2647 this.dropEl.removeClass(['d-none', 'd-block']);
2648 if (action == 'hide') {
2650 this.dropEl.addClass('d-none');
2653 // FIXME - info.card == true!!!
2654 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2656 if (info.card !== true) {
2657 var cardel = info.card.el.dom;
2659 if (info.position == 'above') {
2660 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2661 } else if (cardel.nextSibling) {
2662 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2664 cardel.parentNode.append(this.dropEl.dom);
2667 // card container???
2668 this.containerEl.dom.append(this.dropEl.dom);
2671 this.dropEl.addClass('d-block roo-card-dropzone');
2673 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2680 setHeaderText: function(html)
2683 if (this.headerContainerEl) {
2684 this.headerContainerEl.dom.innerHTML = html;
2694 * Card header - holder for the card header elements.
2699 * @class Roo.bootstrap.CardHeader
2700 * @extends Roo.bootstrap.Element
2701 * Bootstrap CardHeader class
2703 * Create a new Card Header - that you can embed children into
2704 * @param {Object} config The config object
2707 Roo.bootstrap.CardHeader = function(config){
2708 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2711 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2714 container_method : 'getCardHeader'
2727 * Card footer - holder for the card footer elements.
2732 * @class Roo.bootstrap.CardFooter
2733 * @extends Roo.bootstrap.Element
2734 * Bootstrap CardFooter class
2736 * Create a new Card Footer - that you can embed children into
2737 * @param {Object} config The config object
2740 Roo.bootstrap.CardFooter = function(config){
2741 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2744 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2747 container_method : 'getCardFooter'
2760 * Card header - holder for the card header elements.
2765 * @class Roo.bootstrap.CardImageTop
2766 * @extends Roo.bootstrap.Element
2767 * Bootstrap CardImageTop class
2769 * Create a new Card Image Top container
2770 * @param {Object} config The config object
2773 Roo.bootstrap.CardImageTop = function(config){
2774 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2777 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2780 container_method : 'getCardImageTop'
2798 * @class Roo.bootstrap.Img
2799 * @extends Roo.bootstrap.Component
2800 * Bootstrap Img class
2801 * @cfg {Boolean} imgResponsive false | true
2802 * @cfg {String} border rounded | circle | thumbnail
2803 * @cfg {String} src image source
2804 * @cfg {String} alt image alternative text
2805 * @cfg {String} href a tag href
2806 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2807 * @cfg {String} xsUrl xs image source
2808 * @cfg {String} smUrl sm image source
2809 * @cfg {String} mdUrl md image source
2810 * @cfg {String} lgUrl lg image source
2813 * Create a new Input
2814 * @param {Object} config The config object
2817 Roo.bootstrap.Img = function(config){
2818 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2824 * The img click event for the img.
2825 * @param {Roo.EventObject} e
2831 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2833 imgResponsive: true,
2843 getAutoCreate : function()
2845 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2846 return this.createSingleImg();
2851 cls: 'roo-image-responsive-group',
2856 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2858 if(!_this[size + 'Url']){
2864 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2865 html: _this.html || cfg.html,
2866 src: _this[size + 'Url']
2869 img.cls += ' roo-image-responsive-' + size;
2871 var s = ['xs', 'sm', 'md', 'lg'];
2873 s.splice(s.indexOf(size), 1);
2875 Roo.each(s, function(ss){
2876 img.cls += ' hidden-' + ss;
2879 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2880 cfg.cls += ' img-' + _this.border;
2884 cfg.alt = _this.alt;
2897 a.target = _this.target;
2901 cfg.cn.push((_this.href) ? a : img);
2908 createSingleImg : function()
2912 cls: (this.imgResponsive) ? 'img-responsive' : '',
2914 src : 'about:blank' // just incase src get's set to undefined?!?
2917 cfg.html = this.html || cfg.html;
2919 cfg.src = this.src || cfg.src;
2921 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2922 cfg.cls += ' img-' + this.border;
2939 a.target = this.target;
2944 return (this.href) ? a : cfg;
2947 initEvents: function()
2950 this.el.on('click', this.onClick, this);
2955 onClick : function(e)
2957 Roo.log('img onclick');
2958 this.fireEvent('click', this, e);
2961 * Sets the url of the image - used to update it
2962 * @param {String} url the url of the image
2965 setSrc : function(url)
2969 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2970 this.el.dom.src = url;
2974 this.el.select('img', true).first().dom.src = url;
2990 * @class Roo.bootstrap.Link
2991 * @extends Roo.bootstrap.Component
2992 * Bootstrap Link Class
2993 * @cfg {String} alt image alternative text
2994 * @cfg {String} href a tag href
2995 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2996 * @cfg {String} html the content of the link.
2997 * @cfg {String} anchor name for the anchor link
2998 * @cfg {String} fa - favicon
3000 * @cfg {Boolean} preventDefault (true | false) default false
3004 * Create a new Input
3005 * @param {Object} config The config object
3008 Roo.bootstrap.Link = function(config){
3009 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3015 * The img click event for the img.
3016 * @param {Roo.EventObject} e
3022 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3026 preventDefault: false,
3032 getAutoCreate : function()
3034 var html = this.html || '';
3036 if (this.fa !== false) {
3037 html = '<i class="fa fa-' + this.fa + '"></i>';
3042 // anchor's do not require html/href...
3043 if (this.anchor === false) {
3045 cfg.href = this.href || '#';
3047 cfg.name = this.anchor;
3048 if (this.html !== false || this.fa !== false) {
3051 if (this.href !== false) {
3052 cfg.href = this.href;
3056 if(this.alt !== false){
3061 if(this.target !== false) {
3062 cfg.target = this.target;
3068 initEvents: function() {
3070 if(!this.href || this.preventDefault){
3071 this.el.on('click', this.onClick, this);
3075 onClick : function(e)
3077 if(this.preventDefault){
3080 //Roo.log('img onclick');
3081 this.fireEvent('click', this, e);
3094 * @class Roo.bootstrap.Header
3095 * @extends Roo.bootstrap.Component
3096 * Bootstrap Header class
3097 * @cfg {String} html content of header
3098 * @cfg {Number} level (1|2|3|4|5|6) default 1
3101 * Create a new Header
3102 * @param {Object} config The config object
3106 Roo.bootstrap.Header = function(config){
3107 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3110 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3118 getAutoCreate : function(){
3123 tag: 'h' + (1 *this.level),
3124 html: this.html || ''
3136 * Ext JS Library 1.1.1
3137 * Copyright(c) 2006-2007, Ext JS, LLC.
3139 * Originally Released Under LGPL - original licence link has changed is not relivant.
3142 * <script type="text/javascript">
3146 * @class Roo.bootstrap.MenuMgr
3147 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3150 Roo.bootstrap.MenuMgr = function(){
3151 var menus, active, groups = {}, attached = false, lastShow = new Date();
3153 // private - called when first menu is created
3156 active = new Roo.util.MixedCollection();
3157 Roo.get(document).addKeyListener(27, function(){
3158 if(active.length > 0){
3166 if(active && active.length > 0){
3167 var c = active.clone();
3177 if(active.length < 1){
3178 Roo.get(document).un("mouseup", onMouseDown);
3186 var last = active.last();
3187 lastShow = new Date();
3190 Roo.get(document).on("mouseup", onMouseDown);
3195 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3196 m.parentMenu.activeChild = m;
3197 }else if(last && last.isVisible()){
3198 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3203 function onBeforeHide(m){
3205 m.activeChild.hide();
3207 if(m.autoHideTimer){
3208 clearTimeout(m.autoHideTimer);
3209 delete m.autoHideTimer;
3214 function onBeforeShow(m){
3215 var pm = m.parentMenu;
3216 if(!pm && !m.allowOtherMenus){
3218 }else if(pm && pm.activeChild && active != m){
3219 pm.activeChild.hide();
3223 // private this should really trigger on mouseup..
3224 function onMouseDown(e){
3225 Roo.log("on Mouse Up");
3227 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3228 Roo.log("MenuManager hideAll");
3237 function onBeforeCheck(mi, state){
3239 var g = groups[mi.group];
3240 for(var i = 0, l = g.length; i < l; i++){
3242 g[i].setChecked(false);
3251 * Hides all menus that are currently visible
3253 hideAll : function(){
3258 register : function(menu){
3262 menus[menu.id] = menu;
3263 menu.on("beforehide", onBeforeHide);
3264 menu.on("hide", onHide);
3265 menu.on("beforeshow", onBeforeShow);
3266 menu.on("show", onShow);
3268 if(g && menu.events["checkchange"]){
3272 groups[g].push(menu);
3273 menu.on("checkchange", onCheck);
3278 * Returns a {@link Roo.menu.Menu} object
3279 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3280 * be used to generate and return a new Menu instance.
3282 get : function(menu){
3283 if(typeof menu == "string"){ // menu id
3285 }else if(menu.events){ // menu instance
3288 /*else if(typeof menu.length == 'number'){ // array of menu items?
3289 return new Roo.bootstrap.Menu({items:menu});
3290 }else{ // otherwise, must be a config
3291 return new Roo.bootstrap.Menu(menu);
3298 unregister : function(menu){
3299 delete menus[menu.id];
3300 menu.un("beforehide", onBeforeHide);
3301 menu.un("hide", onHide);
3302 menu.un("beforeshow", onBeforeShow);
3303 menu.un("show", onShow);
3305 if(g && menu.events["checkchange"]){
3306 groups[g].remove(menu);
3307 menu.un("checkchange", onCheck);
3312 registerCheckable : function(menuItem){
3313 var g = menuItem.group;
3318 groups[g].push(menuItem);
3319 menuItem.on("beforecheckchange", onBeforeCheck);
3324 unregisterCheckable : function(menuItem){
3325 var g = menuItem.group;
3327 groups[g].remove(menuItem);
3328 menuItem.un("beforecheckchange", onBeforeCheck);
3340 * @class Roo.bootstrap.Menu
3341 * @extends Roo.bootstrap.Component
3342 * Bootstrap Menu class - container for MenuItems
3343 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3344 * @cfg {bool} hidden if the menu should be hidden when rendered.
3345 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3346 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3350 * @param {Object} config The config object
3354 Roo.bootstrap.Menu = function(config){
3355 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3356 if (this.registerMenu && this.type != 'treeview') {
3357 Roo.bootstrap.MenuMgr.register(this);
3364 * Fires before this menu is displayed (return false to block)
3365 * @param {Roo.menu.Menu} this
3370 * Fires before this menu is hidden (return false to block)
3371 * @param {Roo.menu.Menu} this
3376 * Fires after this menu is displayed
3377 * @param {Roo.menu.Menu} this
3382 * Fires after this menu is hidden
3383 * @param {Roo.menu.Menu} this
3388 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3389 * @param {Roo.menu.Menu} this
3390 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3391 * @param {Roo.EventObject} e
3396 * Fires when the mouse is hovering over this menu
3397 * @param {Roo.menu.Menu} this
3398 * @param {Roo.EventObject} e
3399 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3404 * Fires when the mouse exits this menu
3405 * @param {Roo.menu.Menu} this
3406 * @param {Roo.EventObject} e
3407 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3412 * Fires when a menu item contained in this menu is clicked
3413 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3414 * @param {Roo.EventObject} e
3418 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3421 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3425 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3428 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3430 registerMenu : true,
3432 menuItems :false, // stores the menu items..
3442 getChildContainer : function() {
3446 getAutoCreate : function(){
3448 //if (['right'].indexOf(this.align)!==-1) {
3449 // cfg.cn[1].cls += ' pull-right'
3455 cls : 'dropdown-menu' ,
3456 style : 'z-index:1000'
3460 if (this.type === 'submenu') {
3461 cfg.cls = 'submenu active';
3463 if (this.type === 'treeview') {
3464 cfg.cls = 'treeview-menu';
3469 initEvents : function() {
3471 // Roo.log("ADD event");
3472 // Roo.log(this.triggerEl.dom);
3474 this.triggerEl.on('click', this.onTriggerClick, this);
3476 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3479 if (this.triggerEl.hasClass('nav-item')) {
3480 // dropdown toggle on the 'a' in BS4?
3481 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3483 this.triggerEl.addClass('dropdown-toggle');
3486 this.el.on('touchstart' , this.onTouch, this);
3488 this.el.on('click' , this.onClick, this);
3490 this.el.on("mouseover", this.onMouseOver, this);
3491 this.el.on("mouseout", this.onMouseOut, this);
3495 findTargetItem : function(e)
3497 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3501 //Roo.log(t); Roo.log(t.id);
3503 //Roo.log(this.menuitems);
3504 return this.menuitems.get(t.id);
3506 //return this.items.get(t.menuItemId);
3512 onTouch : function(e)
3514 Roo.log("menu.onTouch");
3515 //e.stopEvent(); this make the user popdown broken
3519 onClick : function(e)
3521 Roo.log("menu.onClick");
3523 var t = this.findTargetItem(e);
3524 if(!t || t.isContainer){
3529 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3530 if(t == this.activeItem && t.shouldDeactivate(e)){
3531 this.activeItem.deactivate();
3532 delete this.activeItem;
3536 this.setActiveItem(t, true);
3544 Roo.log('pass click event');
3548 this.fireEvent("click", this, t, e);
3552 if(!t.href.length || t.href == '#'){
3553 (function() { _this.hide(); }).defer(100);
3558 onMouseOver : function(e){
3559 var t = this.findTargetItem(e);
3562 // if(t.canActivate && !t.disabled){
3563 // this.setActiveItem(t, true);
3567 this.fireEvent("mouseover", this, e, t);
3569 isVisible : function(){
3570 return !this.hidden;
3572 onMouseOut : function(e){
3573 var t = this.findTargetItem(e);
3576 // if(t == this.activeItem && t.shouldDeactivate(e)){
3577 // this.activeItem.deactivate();
3578 // delete this.activeItem;
3581 this.fireEvent("mouseout", this, e, t);
3586 * Displays this menu relative to another element
3587 * @param {String/HTMLElement/Roo.Element} element The element to align to
3588 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3589 * the element (defaults to this.defaultAlign)
3590 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3592 show : function(el, pos, parentMenu)
3594 if (false === this.fireEvent("beforeshow", this)) {
3595 Roo.log("show canceled");
3598 this.parentMenu = parentMenu;
3603 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3606 * Displays this menu at a specific xy position
3607 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3608 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3610 showAt : function(xy, parentMenu, /* private: */_e){
3611 this.parentMenu = parentMenu;
3616 this.fireEvent("beforeshow", this);
3617 //xy = this.el.adjustForConstraints(xy);
3621 this.hideMenuItems();
3622 this.hidden = false;
3623 this.triggerEl.addClass('open');
3624 this.el.addClass('show');
3626 // reassign x when hitting right
3627 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3628 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3631 // reassign y when hitting bottom
3632 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3633 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3636 // but the list may align on trigger left or trigger top... should it be a properity?
3638 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3643 this.fireEvent("show", this);
3649 this.doFocus.defer(50, this);
3653 doFocus : function(){
3655 this.focusEl.focus();
3660 * Hides this menu and optionally all parent menus
3661 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3663 hide : function(deep)
3665 if (false === this.fireEvent("beforehide", this)) {
3666 Roo.log("hide canceled");
3669 this.hideMenuItems();
3670 if(this.el && this.isVisible()){
3672 if(this.activeItem){
3673 this.activeItem.deactivate();
3674 this.activeItem = null;
3676 this.triggerEl.removeClass('open');;
3677 this.el.removeClass('show');
3679 this.fireEvent("hide", this);
3681 if(deep === true && this.parentMenu){
3682 this.parentMenu.hide(true);
3686 onTriggerClick : function(e)
3688 Roo.log('trigger click');
3690 var target = e.getTarget();
3692 Roo.log(target.nodeName.toLowerCase());
3694 if(target.nodeName.toLowerCase() === 'i'){
3700 onTriggerPress : function(e)
3702 Roo.log('trigger press');
3703 //Roo.log(e.getTarget());
3704 // Roo.log(this.triggerEl.dom);
3706 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3707 var pel = Roo.get(e.getTarget());
3708 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3709 Roo.log('is treeview or dropdown?');
3713 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3717 if (this.isVisible()) {
3722 this.show(this.triggerEl, '?', false);
3725 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3732 hideMenuItems : function()
3734 Roo.log("hide Menu Items");
3739 this.el.select('.open',true).each(function(aa) {
3741 aa.removeClass('open');
3745 addxtypeChild : function (tree, cntr) {
3746 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3748 this.menuitems.add(comp);
3760 this.getEl().dom.innerHTML = '';
3761 this.menuitems.clear();
3775 * @class Roo.bootstrap.MenuItem
3776 * @extends Roo.bootstrap.Component
3777 * Bootstrap MenuItem class
3778 * @cfg {String} html the menu label
3779 * @cfg {String} href the link
3780 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3781 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3782 * @cfg {Boolean} active used on sidebars to highlight active itesm
3783 * @cfg {String} fa favicon to show on left of menu item.
3784 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3788 * Create a new MenuItem
3789 * @param {Object} config The config object
3793 Roo.bootstrap.MenuItem = function(config){
3794 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3799 * The raw click event for the entire grid.
3800 * @param {Roo.bootstrap.MenuItem} this
3801 * @param {Roo.EventObject} e
3807 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3811 preventDefault: false,
3812 isContainer : false,
3816 getAutoCreate : function(){
3818 if(this.isContainer){
3821 cls: 'dropdown-menu-item '
3831 cls : 'dropdown-item',
3836 if (this.fa !== false) {
3839 cls : 'fa fa-' + this.fa
3848 cls: 'dropdown-menu-item',
3851 if (this.parent().type == 'treeview') {
3852 cfg.cls = 'treeview-menu';
3855 cfg.cls += ' active';
3860 anc.href = this.href || cfg.cn[0].href ;
3861 ctag.html = this.html || cfg.cn[0].html ;
3865 initEvents: function()
3867 if (this.parent().type == 'treeview') {
3868 this.el.select('a').on('click', this.onClick, this);
3872 this.menu.parentType = this.xtype;
3873 this.menu.triggerEl = this.el;
3874 this.menu = this.addxtype(Roo.apply({}, this.menu));
3878 onClick : function(e)
3880 Roo.log('item on click ');
3882 if(this.preventDefault){
3885 //this.parent().hideMenuItems();
3887 this.fireEvent('click', this, e);
3906 * @class Roo.bootstrap.MenuSeparator
3907 * @extends Roo.bootstrap.Component
3908 * Bootstrap MenuSeparator class
3911 * Create a new MenuItem
3912 * @param {Object} config The config object
3916 Roo.bootstrap.MenuSeparator = function(config){
3917 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3920 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3922 getAutoCreate : function(){
3941 * @class Roo.bootstrap.Modal
3942 * @extends Roo.bootstrap.Component
3943 * Bootstrap Modal class
3944 * @cfg {String} title Title of dialog
3945 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3946 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3947 * @cfg {Boolean} specificTitle default false
3948 * @cfg {Array} buttons Array of buttons or standard button set..
3949 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3950 * @cfg {Boolean} animate default true
3951 * @cfg {Boolean} allow_close default true
3952 * @cfg {Boolean} fitwindow default false
3953 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3954 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3955 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3956 * @cfg {String} size (sm|lg|xl) default empty
3957 * @cfg {Number} max_width set the max width of modal
3958 * @cfg {Boolean} editableTitle can the title be edited
3963 * Create a new Modal Dialog
3964 * @param {Object} config The config object
3967 Roo.bootstrap.Modal = function(config){
3968 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3973 * The raw btnclick event for the button
3974 * @param {Roo.EventObject} e
3979 * Fire when dialog resize
3980 * @param {Roo.bootstrap.Modal} this
3981 * @param {Roo.EventObject} e
3985 * @event titlechanged
3986 * Fire when the editable title has been changed
3987 * @param {Roo.bootstrap.Modal} this
3988 * @param {Roo.EventObject} value
3990 "titlechanged" : true
3993 this.buttons = this.buttons || [];
3996 this.tmpl = Roo.factory(this.tmpl);
4001 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4003 title : 'test dialog',
4013 specificTitle: false,
4015 buttonPosition: 'right',
4037 editableTitle : false,
4039 onRender : function(ct, position)
4041 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4044 var cfg = Roo.apply({}, this.getAutoCreate());
4047 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4049 //if (!cfg.name.length) {
4053 cfg.cls += ' ' + this.cls;
4056 cfg.style = this.style;
4058 this.el = Roo.get(document.body).createChild(cfg, position);
4060 //var type = this.el.dom.type;
4063 if(this.tabIndex !== undefined){
4064 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4067 this.dialogEl = this.el.select('.modal-dialog',true).first();
4068 this.bodyEl = this.el.select('.modal-body',true).first();
4069 this.closeEl = this.el.select('.modal-header .close', true).first();
4070 this.headerEl = this.el.select('.modal-header',true).first();
4071 this.titleEl = this.el.select('.modal-title',true).first();
4072 this.footerEl = this.el.select('.modal-footer',true).first();
4074 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4076 //this.el.addClass("x-dlg-modal");
4078 if (this.buttons.length) {
4079 Roo.each(this.buttons, function(bb) {
4080 var b = Roo.apply({}, bb);
4081 b.xns = b.xns || Roo.bootstrap;
4082 b.xtype = b.xtype || 'Button';
4083 if (typeof(b.listeners) == 'undefined') {
4084 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4087 var btn = Roo.factory(b);
4089 btn.render(this.getButtonContainer());
4093 // render the children.
4096 if(typeof(this.items) != 'undefined'){
4097 var items = this.items;
4100 for(var i =0;i < items.length;i++) {
4101 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4105 this.items = nitems;
4107 // where are these used - they used to be body/close/footer
4111 //this.el.addClass([this.fieldClass, this.cls]);
4115 getAutoCreate : function()
4117 // we will default to modal-body-overflow - might need to remove or make optional later.
4119 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4120 html : this.html || ''
4125 cls : 'modal-title',
4129 if(this.specificTitle){ // WTF is this?
4134 if (this.allow_close && Roo.bootstrap.version == 3) {
4144 if (this.editableTitle) {
4146 cls: 'form-control roo-editable-title d-none',
4152 if (this.allow_close && Roo.bootstrap.version == 4) {
4162 if(this.size.length){
4163 size = 'modal-' + this.size;
4166 var footer = Roo.bootstrap.version == 3 ?
4168 cls : 'modal-footer',
4172 cls: 'btn-' + this.buttonPosition
4177 { // BS4 uses mr-auto on left buttons....
4178 cls : 'modal-footer'
4189 cls: "modal-dialog " + size,
4192 cls : "modal-content",
4195 cls : 'modal-header',
4210 modal.cls += ' fade';
4216 getChildContainer : function() {
4221 getButtonContainer : function() {
4223 return Roo.bootstrap.version == 4 ?
4224 this.el.select('.modal-footer',true).first()
4225 : this.el.select('.modal-footer div',true).first();
4228 initEvents : function()
4230 if (this.allow_close) {
4231 this.closeEl.on('click', this.hide, this);
4233 Roo.EventManager.onWindowResize(this.resize, this, true);
4234 if (this.editableTitle) {
4235 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4236 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4237 this.headerEditEl.on('keyup', function(e) {
4238 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4239 this.toggleHeaderInput(false)
4242 this.headerEditEl.on('blur', function(e) {
4243 this.toggleHeaderInput(false)
4252 this.maskEl.setSize(
4253 Roo.lib.Dom.getViewWidth(true),
4254 Roo.lib.Dom.getViewHeight(true)
4257 if (this.fitwindow) {
4259 this.dialogEl.setStyle( { 'max-width' : '100%' });
4261 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4262 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4267 if(this.max_width !== 0) {
4269 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4272 this.setSize(w, this.height);
4276 if(this.max_height) {
4277 this.setSize(w,Math.min(
4279 Roo.lib.Dom.getViewportHeight(true) - 60
4285 if(!this.fit_content) {
4286 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4290 this.setSize(w, Math.min(
4292 this.headerEl.getHeight() +
4293 this.footerEl.getHeight() +
4294 this.getChildHeight(this.bodyEl.dom.childNodes),
4295 Roo.lib.Dom.getViewportHeight(true) - 60)
4301 setSize : function(w,h)
4312 if (!this.rendered) {
4315 this.toggleHeaderInput(false);
4316 //this.el.setStyle('display', 'block');
4317 this.el.removeClass('hideing');
4318 this.el.dom.style.display='block';
4320 Roo.get(document.body).addClass('modal-open');
4322 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4325 this.el.addClass('show');
4326 this.el.addClass('in');
4329 this.el.addClass('show');
4330 this.el.addClass('in');
4333 // not sure how we can show data in here..
4335 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4338 Roo.get(document.body).addClass("x-body-masked");
4340 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4341 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4342 this.maskEl.dom.style.display = 'block';
4343 this.maskEl.addClass('show');
4348 this.fireEvent('show', this);
4350 // set zindex here - otherwise it appears to be ignored...
4351 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4354 this.items.forEach( function(e) {
4355 e.layout ? e.layout() : false;
4363 if(this.fireEvent("beforehide", this) !== false){
4365 this.maskEl.removeClass('show');
4367 this.maskEl.dom.style.display = '';
4368 Roo.get(document.body).removeClass("x-body-masked");
4369 this.el.removeClass('in');
4370 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4372 if(this.animate){ // why
4373 this.el.addClass('hideing');
4374 this.el.removeClass('show');
4376 if (!this.el.hasClass('hideing')) {
4377 return; // it's been shown again...
4380 this.el.dom.style.display='';
4382 Roo.get(document.body).removeClass('modal-open');
4383 this.el.removeClass('hideing');
4387 this.el.removeClass('show');
4388 this.el.dom.style.display='';
4389 Roo.get(document.body).removeClass('modal-open');
4392 this.fireEvent('hide', this);
4395 isVisible : function()
4398 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4402 addButton : function(str, cb)
4406 var b = Roo.apply({}, { html : str } );
4407 b.xns = b.xns || Roo.bootstrap;
4408 b.xtype = b.xtype || 'Button';
4409 if (typeof(b.listeners) == 'undefined') {
4410 b.listeners = { click : cb.createDelegate(this) };
4413 var btn = Roo.factory(b);
4415 btn.render(this.getButtonContainer());
4421 setDefaultButton : function(btn)
4423 //this.el.select('.modal-footer').()
4426 resizeTo: function(w,h)
4428 this.dialogEl.setWidth(w);
4430 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4432 this.bodyEl.setHeight(h - diff);
4434 this.fireEvent('resize', this);
4437 setContentSize : function(w, h)
4441 onButtonClick: function(btn,e)
4444 this.fireEvent('btnclick', btn.name, e);
4447 * Set the title of the Dialog
4448 * @param {String} str new Title
4450 setTitle: function(str) {
4451 this.titleEl.dom.innerHTML = str;
4455 * Set the body of the Dialog
4456 * @param {String} str new Title
4458 setBody: function(str) {
4459 this.bodyEl.dom.innerHTML = str;
4462 * Set the body of the Dialog using the template
4463 * @param {Obj} data - apply this data to the template and replace the body contents.
4465 applyBody: function(obj)
4468 Roo.log("Error - using apply Body without a template");
4471 this.tmpl.overwrite(this.bodyEl, obj);
4474 getChildHeight : function(child_nodes)
4478 child_nodes.length == 0
4483 var child_height = 0;
4485 for(var i = 0; i < child_nodes.length; i++) {
4488 * for modal with tabs...
4489 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4491 var layout_childs = child_nodes[i].childNodes;
4493 for(var j = 0; j < layout_childs.length; j++) {
4495 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4497 var layout_body_childs = layout_childs[j].childNodes;
4499 for(var k = 0; k < layout_body_childs.length; k++) {
4501 if(layout_body_childs[k].classList.contains('navbar')) {
4502 child_height += layout_body_childs[k].offsetHeight;
4506 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4508 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4510 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4512 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4513 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4528 child_height += child_nodes[i].offsetHeight;
4529 // Roo.log(child_nodes[i].offsetHeight);
4532 return child_height;
4534 toggleHeaderInput : function(is_edit)
4536 if (!this.editableTitle) {
4537 return; // not editable.
4539 if (is_edit && this.is_header_editing) {
4540 return; // already editing..
4544 this.headerEditEl.dom.value = this.title;
4545 this.headerEditEl.removeClass('d-none');
4546 this.headerEditEl.dom.focus();
4547 this.titleEl.addClass('d-none');
4549 this.is_header_editing = true;
4552 // flip back to not editing.
4553 this.title = this.headerEditEl.dom.value;
4554 this.headerEditEl.addClass('d-none');
4555 this.titleEl.removeClass('d-none');
4556 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4557 this.is_header_editing = false;
4558 this.fireEvent('titlechanged', this, this.title);
4567 Roo.apply(Roo.bootstrap.Modal, {
4569 * Button config that displays a single OK button
4578 * Button config that displays Yes and No buttons
4594 * Button config that displays OK and Cancel buttons
4609 * Button config that displays Yes, No and Cancel buttons
4634 * messagebox - can be used as a replace
4638 * @class Roo.MessageBox
4639 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4643 Roo.Msg.alert('Status', 'Changes saved successfully.');
4645 // Prompt for user data:
4646 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4648 // process text value...
4652 // Show a dialog using config options:
4654 title:'Save Changes?',
4655 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4656 buttons: Roo.Msg.YESNOCANCEL,
4663 Roo.bootstrap.MessageBox = function(){
4664 var dlg, opt, mask, waitTimer;
4665 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4666 var buttons, activeTextEl, bwidth;
4670 var handleButton = function(button){
4672 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4676 var handleHide = function(){
4678 dlg.el.removeClass(opt.cls);
4681 // Roo.TaskMgr.stop(waitTimer);
4682 // waitTimer = null;
4687 var updateButtons = function(b){
4690 buttons["ok"].hide();
4691 buttons["cancel"].hide();
4692 buttons["yes"].hide();
4693 buttons["no"].hide();
4694 dlg.footerEl.hide();
4698 dlg.footerEl.show();
4699 for(var k in buttons){
4700 if(typeof buttons[k] != "function"){
4703 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4704 width += buttons[k].el.getWidth()+15;
4714 var handleEsc = function(d, k, e){
4715 if(opt && opt.closable !== false){
4725 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4726 * @return {Roo.BasicDialog} The BasicDialog element
4728 getDialog : function(){
4730 dlg = new Roo.bootstrap.Modal( {
4733 //constraintoviewport:false,
4735 //collapsible : false,
4740 //buttonAlign:"center",
4741 closeClick : function(){
4742 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4745 handleButton("cancel");
4750 dlg.on("hide", handleHide);
4752 //dlg.addKeyListener(27, handleEsc);
4754 this.buttons = buttons;
4755 var bt = this.buttonText;
4756 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4757 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4758 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4759 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4761 bodyEl = dlg.bodyEl.createChild({
4763 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4764 '<textarea class="roo-mb-textarea"></textarea>' +
4765 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4767 msgEl = bodyEl.dom.firstChild;
4768 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4769 textboxEl.enableDisplayMode();
4770 textboxEl.addKeyListener([10,13], function(){
4771 if(dlg.isVisible() && opt && opt.buttons){
4774 }else if(opt.buttons.yes){
4775 handleButton("yes");
4779 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4780 textareaEl.enableDisplayMode();
4781 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4782 progressEl.enableDisplayMode();
4784 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4785 var pf = progressEl.dom.firstChild;
4787 pp = Roo.get(pf.firstChild);
4788 pp.setHeight(pf.offsetHeight);
4796 * Updates the message box body text
4797 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4798 * the XHTML-compliant non-breaking space character '&#160;')
4799 * @return {Roo.MessageBox} This message box
4801 updateText : function(text)
4803 if(!dlg.isVisible() && !opt.width){
4804 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4805 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4807 msgEl.innerHTML = text || ' ';
4809 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4810 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4812 Math.min(opt.width || cw , this.maxWidth),
4813 Math.max(opt.minWidth || this.minWidth, bwidth)
4816 activeTextEl.setWidth(w);
4818 if(dlg.isVisible()){
4819 dlg.fixedcenter = false;
4821 // to big, make it scroll. = But as usual stupid IE does not support
4824 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4825 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4826 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4828 bodyEl.dom.style.height = '';
4829 bodyEl.dom.style.overflowY = '';
4832 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4834 bodyEl.dom.style.overflowX = '';
4837 dlg.setContentSize(w, bodyEl.getHeight());
4838 if(dlg.isVisible()){
4839 dlg.fixedcenter = true;
4845 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4846 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4847 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4848 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4849 * @return {Roo.MessageBox} This message box
4851 updateProgress : function(value, text){
4853 this.updateText(text);
4856 if (pp) { // weird bug on my firefox - for some reason this is not defined
4857 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4858 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4864 * Returns true if the message box is currently displayed
4865 * @return {Boolean} True if the message box is visible, else false
4867 isVisible : function(){
4868 return dlg && dlg.isVisible();
4872 * Hides the message box if it is displayed
4875 if(this.isVisible()){
4881 * Displays a new message box, or reinitializes an existing message box, based on the config options
4882 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4883 * The following config object properties are supported:
4885 Property Type Description
4886 ---------- --------------- ------------------------------------------------------------------------------------
4887 animEl String/Element An id or Element from which the message box should animate as it opens and
4888 closes (defaults to undefined)
4889 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4890 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4891 closable Boolean False to hide the top-right close button (defaults to true). Note that
4892 progress and wait dialogs will ignore this property and always hide the
4893 close button as they can only be closed programmatically.
4894 cls String A custom CSS class to apply to the message box element
4895 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4896 displayed (defaults to 75)
4897 fn Function A callback function to execute after closing the dialog. The arguments to the
4898 function will be btn (the name of the button that was clicked, if applicable,
4899 e.g. "ok"), and text (the value of the active text field, if applicable).
4900 Progress and wait dialogs will ignore this option since they do not respond to
4901 user actions and can only be closed programmatically, so any required function
4902 should be called by the same code after it closes the dialog.
4903 icon String A CSS class that provides a background image to be used as an icon for
4904 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4905 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4906 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4907 modal Boolean False to allow user interaction with the page while the message box is
4908 displayed (defaults to true)
4909 msg String A string that will replace the existing message box body text (defaults
4910 to the XHTML-compliant non-breaking space character ' ')
4911 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4912 progress Boolean True to display a progress bar (defaults to false)
4913 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4914 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4915 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4916 title String The title text
4917 value String The string value to set into the active textbox element if displayed
4918 wait Boolean True to display a progress bar (defaults to false)
4919 width Number The width of the dialog in pixels
4926 msg: 'Please enter your address:',
4928 buttons: Roo.MessageBox.OKCANCEL,
4931 animEl: 'addAddressBtn'
4934 * @param {Object} config Configuration options
4935 * @return {Roo.MessageBox} This message box
4937 show : function(options)
4940 // this causes nightmares if you show one dialog after another
4941 // especially on callbacks..
4943 if(this.isVisible()){
4946 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4947 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4948 Roo.log("New Dialog Message:" + options.msg )
4949 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4950 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4953 var d = this.getDialog();
4955 d.setTitle(opt.title || " ");
4956 d.closeEl.setDisplayed(opt.closable !== false);
4957 activeTextEl = textboxEl;
4958 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4963 textareaEl.setHeight(typeof opt.multiline == "number" ?
4964 opt.multiline : this.defaultTextHeight);
4965 activeTextEl = textareaEl;
4974 progressEl.setDisplayed(opt.progress === true);
4976 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4978 this.updateProgress(0);
4979 activeTextEl.dom.value = opt.value || "";
4981 dlg.setDefaultButton(activeTextEl);
4983 var bs = opt.buttons;
4987 }else if(bs && bs.yes){
4988 db = buttons["yes"];
4990 dlg.setDefaultButton(db);
4992 bwidth = updateButtons(opt.buttons);
4993 this.updateText(opt.msg);
4995 d.el.addClass(opt.cls);
4997 d.proxyDrag = opt.proxyDrag === true;
4998 d.modal = opt.modal !== false;
4999 d.mask = opt.modal !== false ? mask : false;
5001 // force it to the end of the z-index stack so it gets a cursor in FF
5002 document.body.appendChild(dlg.el.dom);
5003 d.animateTarget = null;
5004 d.show(options.animEl);
5010 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5011 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5012 * and closing the message box when the process is complete.
5013 * @param {String} title The title bar text
5014 * @param {String} msg The message box body text
5015 * @return {Roo.MessageBox} This message box
5017 progress : function(title, msg){
5024 minWidth: this.minProgressWidth,
5031 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5032 * If a callback function is passed it will be called after the user clicks the button, and the
5033 * id of the button that was clicked will be passed as the only parameter to the callback
5034 * (could also be the top-right close button).
5035 * @param {String} title The title bar text
5036 * @param {String} msg The message box body text
5037 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5038 * @param {Object} scope (optional) The scope of the callback function
5039 * @return {Roo.MessageBox} This message box
5041 alert : function(title, msg, fn, scope)
5056 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5057 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5058 * You are responsible for closing the message box when the process is complete.
5059 * @param {String} msg The message box body text
5060 * @param {String} title (optional) The title bar text
5061 * @return {Roo.MessageBox} This message box
5063 wait : function(msg, title){
5074 waitTimer = Roo.TaskMgr.start({
5076 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5084 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5085 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5086 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5087 * @param {String} title The title bar text
5088 * @param {String} msg The message box body text
5089 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5090 * @param {Object} scope (optional) The scope of the callback function
5091 * @return {Roo.MessageBox} This message box
5093 confirm : function(title, msg, fn, scope){
5097 buttons: this.YESNO,
5106 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5107 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5108 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5109 * (could also be the top-right close button) and the text that was entered will be passed as the two
5110 * parameters to the callback.
5111 * @param {String} title The title bar text
5112 * @param {String} msg The message box body text
5113 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5114 * @param {Object} scope (optional) The scope of the callback function
5115 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5116 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5117 * @return {Roo.MessageBox} This message box
5119 prompt : function(title, msg, fn, scope, multiline){
5123 buttons: this.OKCANCEL,
5128 multiline: multiline,
5135 * Button config that displays a single OK button
5140 * Button config that displays Yes and No buttons
5143 YESNO : {yes:true, no:true},
5145 * Button config that displays OK and Cancel buttons
5148 OKCANCEL : {ok:true, cancel:true},
5150 * Button config that displays Yes, No and Cancel buttons
5153 YESNOCANCEL : {yes:true, no:true, cancel:true},
5156 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5159 defaultTextHeight : 75,
5161 * The maximum width in pixels of the message box (defaults to 600)
5166 * The minimum width in pixels of the message box (defaults to 100)
5171 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5172 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5175 minProgressWidth : 250,
5177 * An object containing the default button text strings that can be overriden for localized language support.
5178 * Supported properties are: ok, cancel, yes and no.
5179 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5192 * Shorthand for {@link Roo.MessageBox}
5194 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5195 Roo.Msg = Roo.Msg || Roo.MessageBox;
5204 * @class Roo.bootstrap.Navbar
5205 * @extends Roo.bootstrap.Component
5206 * Bootstrap Navbar class
5209 * Create a new Navbar
5210 * @param {Object} config The config object
5214 Roo.bootstrap.Navbar = function(config){
5215 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5219 * @event beforetoggle
5220 * Fire before toggle the menu
5221 * @param {Roo.EventObject} e
5223 "beforetoggle" : true
5227 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5236 getAutoCreate : function(){
5239 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5243 initEvents :function ()
5245 //Roo.log(this.el.select('.navbar-toggle',true));
5246 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5253 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5255 var size = this.el.getSize();
5256 this.maskEl.setSize(size.width, size.height);
5257 this.maskEl.enableDisplayMode("block");
5266 getChildContainer : function()
5268 if (this.el && this.el.select('.collapse').getCount()) {
5269 return this.el.select('.collapse',true).first();
5284 onToggle : function()
5287 if(this.fireEvent('beforetoggle', this) === false){
5290 var ce = this.el.select('.navbar-collapse',true).first();
5292 if (!ce.hasClass('show')) {
5302 * Expand the navbar pulldown
5304 expand : function ()
5307 var ce = this.el.select('.navbar-collapse',true).first();
5308 if (ce.hasClass('collapsing')) {
5311 ce.dom.style.height = '';
5313 ce.addClass('in'); // old...
5314 ce.removeClass('collapse');
5315 ce.addClass('show');
5316 var h = ce.getHeight();
5318 ce.removeClass('show');
5319 // at this point we should be able to see it..
5320 ce.addClass('collapsing');
5322 ce.setHeight(0); // resize it ...
5323 ce.on('transitionend', function() {
5324 //Roo.log('done transition');
5325 ce.removeClass('collapsing');
5326 ce.addClass('show');
5327 ce.removeClass('collapse');
5329 ce.dom.style.height = '';
5330 }, this, { single: true} );
5332 ce.dom.scrollTop = 0;
5335 * Collapse the navbar pulldown
5337 collapse : function()
5339 var ce = this.el.select('.navbar-collapse',true).first();
5341 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5342 // it's collapsed or collapsing..
5345 ce.removeClass('in'); // old...
5346 ce.setHeight(ce.getHeight());
5347 ce.removeClass('show');
5348 ce.addClass('collapsing');
5350 ce.on('transitionend', function() {
5351 ce.dom.style.height = '';
5352 ce.removeClass('collapsing');
5353 ce.addClass('collapse');
5354 }, this, { single: true} );
5374 * @class Roo.bootstrap.NavSimplebar
5375 * @extends Roo.bootstrap.Navbar
5376 * Bootstrap Sidebar class
5378 * @cfg {Boolean} inverse is inverted color
5380 * @cfg {String} type (nav | pills | tabs)
5381 * @cfg {Boolean} arrangement stacked | justified
5382 * @cfg {String} align (left | right) alignment
5384 * @cfg {Boolean} main (true|false) main nav bar? default false
5385 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5387 * @cfg {String} tag (header|footer|nav|div) default is nav
5389 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5393 * Create a new Sidebar
5394 * @param {Object} config The config object
5398 Roo.bootstrap.NavSimplebar = function(config){
5399 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5402 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5418 getAutoCreate : function(){
5422 tag : this.tag || 'div',
5423 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5425 if (['light','white'].indexOf(this.weight) > -1) {
5426 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5428 cfg.cls += ' bg-' + this.weight;
5431 cfg.cls += ' navbar-inverse';
5435 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5437 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5446 cls: 'nav nav-' + this.xtype,
5452 this.type = this.type || 'nav';
5453 if (['tabs','pills'].indexOf(this.type) != -1) {
5454 cfg.cn[0].cls += ' nav-' + this.type
5458 if (this.type!=='nav') {
5459 Roo.log('nav type must be nav/tabs/pills')
5461 cfg.cn[0].cls += ' navbar-nav'
5467 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5468 cfg.cn[0].cls += ' nav-' + this.arrangement;
5472 if (this.align === 'right') {
5473 cfg.cn[0].cls += ' navbar-right';
5498 * navbar-expand-md fixed-top
5502 * @class Roo.bootstrap.NavHeaderbar
5503 * @extends Roo.bootstrap.NavSimplebar
5504 * Bootstrap Sidebar class
5506 * @cfg {String} brand what is brand
5507 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5508 * @cfg {String} brand_href href of the brand
5509 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5510 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5511 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5512 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5515 * Create a new Sidebar
5516 * @param {Object} config The config object
5520 Roo.bootstrap.NavHeaderbar = function(config){
5521 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5525 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5532 desktopCenter : false,
5535 getAutoCreate : function(){
5538 tag: this.nav || 'nav',
5539 cls: 'navbar navbar-expand-md',
5545 if (this.desktopCenter) {
5546 cn.push({cls : 'container', cn : []});
5554 cls: 'navbar-toggle navbar-toggler',
5555 'data-toggle': 'collapse',
5560 html: 'Toggle navigation'
5564 cls: 'icon-bar navbar-toggler-icon'
5577 cn.push( Roo.bootstrap.version == 4 ? btn : {
5579 cls: 'navbar-header',
5588 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5592 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5594 if (['light','white'].indexOf(this.weight) > -1) {
5595 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5597 cfg.cls += ' bg-' + this.weight;
5600 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5601 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5603 // tag can override this..
5605 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5608 if (this.brand !== '') {
5609 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5610 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5612 href: this.brand_href ? this.brand_href : '#',
5613 cls: 'navbar-brand',
5621 cfg.cls += ' main-nav';
5629 getHeaderChildContainer : function()
5631 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5632 return this.el.select('.navbar-header',true).first();
5635 return this.getChildContainer();
5638 getChildContainer : function()
5641 return this.el.select('.roo-navbar-collapse',true).first();
5646 initEvents : function()
5648 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5650 if (this.autohide) {
5655 Roo.get(document).on('scroll',function(e) {
5656 var ns = Roo.get(document).getScroll().top;
5657 var os = prevScroll;
5661 ft.removeClass('slideDown');
5662 ft.addClass('slideUp');
5665 ft.removeClass('slideUp');
5666 ft.addClass('slideDown');
5687 * @class Roo.bootstrap.NavSidebar
5688 * @extends Roo.bootstrap.Navbar
5689 * Bootstrap Sidebar class
5692 * Create a new Sidebar
5693 * @param {Object} config The config object
5697 Roo.bootstrap.NavSidebar = function(config){
5698 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5701 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5703 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5705 getAutoCreate : function(){
5710 cls: 'sidebar sidebar-nav'
5732 * @class Roo.bootstrap.NavGroup
5733 * @extends Roo.bootstrap.Component
5734 * Bootstrap NavGroup class
5735 * @cfg {String} align (left|right)
5736 * @cfg {Boolean} inverse
5737 * @cfg {String} type (nav|pills|tab) default nav
5738 * @cfg {String} navId - reference Id for navbar.
5739 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5742 * Create a new nav group
5743 * @param {Object} config The config object
5746 Roo.bootstrap.NavGroup = function(config){
5747 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5750 Roo.bootstrap.NavGroup.register(this);
5754 * Fires when the active item changes
5755 * @param {Roo.bootstrap.NavGroup} this
5756 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5757 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5764 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5776 getAutoCreate : function()
5778 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5784 if (Roo.bootstrap.version == 4) {
5785 if (['tabs','pills'].indexOf(this.type) != -1) {
5786 cfg.cls += ' nav-' + this.type;
5788 // trying to remove so header bar can right align top?
5789 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5790 // do not use on header bar...
5791 cfg.cls += ' navbar-nav';
5796 if (['tabs','pills'].indexOf(this.type) != -1) {
5797 cfg.cls += ' nav-' + this.type
5799 if (this.type !== 'nav') {
5800 Roo.log('nav type must be nav/tabs/pills')
5802 cfg.cls += ' navbar-nav'
5806 if (this.parent() && this.parent().sidebar) {
5809 cls: 'dashboard-menu sidebar-menu'
5815 if (this.form === true) {
5818 cls: 'navbar-form form-inline'
5820 //nav navbar-right ml-md-auto
5821 if (this.align === 'right') {
5822 cfg.cls += ' navbar-right ml-md-auto';
5824 cfg.cls += ' navbar-left';
5828 if (this.align === 'right') {
5829 cfg.cls += ' navbar-right ml-md-auto';
5831 cfg.cls += ' mr-auto';
5835 cfg.cls += ' navbar-inverse';
5843 * sets the active Navigation item
5844 * @param {Roo.bootstrap.NavItem} the new current navitem
5846 setActiveItem : function(item)
5849 Roo.each(this.navItems, function(v){
5854 v.setActive(false, true);
5861 item.setActive(true, true);
5862 this.fireEvent('changed', this, item, prev);
5867 * gets the active Navigation item
5868 * @return {Roo.bootstrap.NavItem} the current navitem
5870 getActive : function()
5874 Roo.each(this.navItems, function(v){
5885 indexOfNav : function()
5889 Roo.each(this.navItems, function(v,i){
5900 * adds a Navigation item
5901 * @param {Roo.bootstrap.NavItem} the navitem to add
5903 addItem : function(cfg)
5905 if (this.form && Roo.bootstrap.version == 4) {
5908 var cn = new Roo.bootstrap.NavItem(cfg);
5910 cn.parentId = this.id;
5911 cn.onRender(this.el, null);
5915 * register a Navigation item
5916 * @param {Roo.bootstrap.NavItem} the navitem to add
5918 register : function(item)
5920 this.navItems.push( item);
5921 item.navId = this.navId;
5926 * clear all the Navigation item
5929 clearAll : function()
5932 this.el.dom.innerHTML = '';
5935 getNavItem: function(tabId)
5938 Roo.each(this.navItems, function(e) {
5939 if (e.tabId == tabId) {
5949 setActiveNext : function()
5951 var i = this.indexOfNav(this.getActive());
5952 if (i > this.navItems.length) {
5955 this.setActiveItem(this.navItems[i+1]);
5957 setActivePrev : function()
5959 var i = this.indexOfNav(this.getActive());
5963 this.setActiveItem(this.navItems[i-1]);
5965 clearWasActive : function(except) {
5966 Roo.each(this.navItems, function(e) {
5967 if (e.tabId != except.tabId && e.was_active) {
5968 e.was_active = false;
5975 getWasActive : function ()
5978 Roo.each(this.navItems, function(e) {
5993 Roo.apply(Roo.bootstrap.NavGroup, {
5997 * register a Navigation Group
5998 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6000 register : function(navgrp)
6002 this.groups[navgrp.navId] = navgrp;
6006 * fetch a Navigation Group based on the navigation ID
6007 * @param {string} the navgroup to add
6008 * @returns {Roo.bootstrap.NavGroup} the navgroup
6010 get: function(navId) {
6011 if (typeof(this.groups[navId]) == 'undefined') {
6013 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6015 return this.groups[navId] ;
6030 * @class Roo.bootstrap.NavItem
6031 * @extends Roo.bootstrap.Component
6032 * Bootstrap Navbar.NavItem class
6033 * @cfg {String} href link to
6034 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6035 * @cfg {Boolean} button_outline show and outlined button
6036 * @cfg {String} html content of button
6037 * @cfg {String} badge text inside badge
6038 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6039 * @cfg {String} glyphicon DEPRICATED - use fa
6040 * @cfg {String} icon DEPRICATED - use fa
6041 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6042 * @cfg {Boolean} active Is item active
6043 * @cfg {Boolean} disabled Is item disabled
6044 * @cfg {String} linkcls Link Class
6045 * @cfg {Boolean} preventDefault (true | false) default false
6046 * @cfg {String} tabId the tab that this item activates.
6047 * @cfg {String} tagtype (a|span) render as a href or span?
6048 * @cfg {Boolean} animateRef (true|false) link to element default false
6051 * Create a new Navbar Item
6052 * @param {Object} config The config object
6054 Roo.bootstrap.NavItem = function(config){
6055 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6060 * The raw click event for the entire grid.
6061 * @param {Roo.EventObject} e
6066 * Fires when the active item active state changes
6067 * @param {Roo.bootstrap.NavItem} this
6068 * @param {boolean} state the new state
6074 * Fires when scroll to element
6075 * @param {Roo.bootstrap.NavItem} this
6076 * @param {Object} options
6077 * @param {Roo.EventObject} e
6085 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6094 preventDefault : false,
6102 button_outline : false,
6106 getAutoCreate : function(){
6113 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6116 cfg.cls += ' active' ;
6118 if (this.disabled) {
6119 cfg.cls += ' disabled';
6123 if (this.button_weight.length) {
6124 cfg.tag = this.href ? 'a' : 'button';
6125 cfg.html = this.html || '';
6126 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6128 cfg.href = this.href;
6131 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6134 // menu .. should add dropdown-menu class - so no need for carat..
6136 if (this.badge !== '') {
6138 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6143 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6147 href : this.href || "#",
6148 html: this.html || ''
6151 if (this.tagtype == 'a') {
6152 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6156 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6159 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6161 if(this.glyphicon) {
6162 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6167 cfg.cn[0].html += " <span class='caret'></span>";
6171 if (this.badge !== '') {
6173 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6181 onRender : function(ct, position)
6183 // Roo.log("Call onRender: " + this.xtype);
6184 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6188 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6189 this.navLink = this.el.select('.nav-link',true).first();
6194 initEvents: function()
6196 if (typeof (this.menu) != 'undefined') {
6197 this.menu.parentType = this.xtype;
6198 this.menu.triggerEl = this.el;
6199 this.menu = this.addxtype(Roo.apply({}, this.menu));
6202 this.el.on('click', this.onClick, this);
6204 //if(this.tagtype == 'span'){
6205 // this.el.select('span',true).on('click', this.onClick, this);
6208 // at this point parent should be available..
6209 this.parent().register(this);
6212 onClick : function(e)
6214 if (e.getTarget('.dropdown-menu-item')) {
6215 // did you click on a menu itemm.... - then don't trigger onclick..
6220 this.preventDefault ||
6223 Roo.log("NavItem - prevent Default?");
6227 if (this.disabled) {
6231 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6232 if (tg && tg.transition) {
6233 Roo.log("waiting for the transitionend");
6239 //Roo.log("fire event clicked");
6240 if(this.fireEvent('click', this, e) === false){
6244 if(this.tagtype == 'span'){
6248 //Roo.log(this.href);
6249 var ael = this.el.select('a',true).first();
6252 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6253 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6254 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6255 return; // ignore... - it's a 'hash' to another page.
6257 Roo.log("NavItem - prevent Default?");
6259 this.scrollToElement(e);
6263 var p = this.parent();
6265 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6266 if (typeof(p.setActiveItem) !== 'undefined') {
6267 p.setActiveItem(this);
6271 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6272 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6273 // remove the collapsed menu expand...
6274 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6278 isActive: function () {
6281 setActive : function(state, fire, is_was_active)
6283 if (this.active && !state && this.navId) {
6284 this.was_active = true;
6285 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6287 nv.clearWasActive(this);
6291 this.active = state;
6294 this.el.removeClass('active');
6295 this.navLink ? this.navLink.removeClass('active') : false;
6296 } else if (!this.el.hasClass('active')) {
6298 this.el.addClass('active');
6299 if (Roo.bootstrap.version == 4 && this.navLink ) {
6300 this.navLink.addClass('active');
6305 this.fireEvent('changed', this, state);
6308 // show a panel if it's registered and related..
6310 if (!this.navId || !this.tabId || !state || is_was_active) {
6314 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6318 var pan = tg.getPanelByName(this.tabId);
6322 // if we can not flip to new panel - go back to old nav highlight..
6323 if (false == tg.showPanel(pan)) {
6324 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6326 var onav = nv.getWasActive();
6328 onav.setActive(true, false, true);
6337 // this should not be here...
6338 setDisabled : function(state)
6340 this.disabled = state;
6342 this.el.removeClass('disabled');
6343 } else if (!this.el.hasClass('disabled')) {
6344 this.el.addClass('disabled');
6350 * Fetch the element to display the tooltip on.
6351 * @return {Roo.Element} defaults to this.el
6353 tooltipEl : function()
6355 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6358 scrollToElement : function(e)
6360 var c = document.body;
6363 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6365 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6366 c = document.documentElement;
6369 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6375 var o = target.calcOffsetsTo(c);
6382 this.fireEvent('scrollto', this, options, e);
6384 Roo.get(c).scrollTo('top', options.value, true);
6397 * <span> icon </span>
6398 * <span> text </span>
6399 * <span>badge </span>
6403 * @class Roo.bootstrap.NavSidebarItem
6404 * @extends Roo.bootstrap.NavItem
6405 * Bootstrap Navbar.NavSidebarItem class
6406 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6407 * {Boolean} open is the menu open
6408 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6409 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6410 * {String} buttonSize (sm|md|lg)the extra classes for the button
6411 * {Boolean} showArrow show arrow next to the text (default true)
6413 * Create a new Navbar Button
6414 * @param {Object} config The config object
6416 Roo.bootstrap.NavSidebarItem = function(config){
6417 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6422 * The raw click event for the entire grid.
6423 * @param {Roo.EventObject} e
6428 * Fires when the active item active state changes
6429 * @param {Roo.bootstrap.NavSidebarItem} this
6430 * @param {boolean} state the new state
6438 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6440 badgeWeight : 'default',
6446 buttonWeight : 'default',
6452 getAutoCreate : function(){
6457 href : this.href || '#',
6463 if(this.buttonView){
6466 href : this.href || '#',
6467 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6480 cfg.cls += ' active';
6483 if (this.disabled) {
6484 cfg.cls += ' disabled';
6487 cfg.cls += ' open x-open';
6490 if (this.glyphicon || this.icon) {
6491 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6492 a.cn.push({ tag : 'i', cls : c }) ;
6495 if(!this.buttonView){
6498 html : this.html || ''
6505 if (this.badge !== '') {
6506 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6512 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6515 a.cls += ' dropdown-toggle treeview' ;
6521 initEvents : function()
6523 if (typeof (this.menu) != 'undefined') {
6524 this.menu.parentType = this.xtype;
6525 this.menu.triggerEl = this.el;
6526 this.menu = this.addxtype(Roo.apply({}, this.menu));
6529 this.el.on('click', this.onClick, this);
6531 if(this.badge !== ''){
6532 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6537 onClick : function(e)
6544 if(this.preventDefault){
6548 this.fireEvent('click', this, e);
6551 disable : function()
6553 this.setDisabled(true);
6558 this.setDisabled(false);
6561 setDisabled : function(state)
6563 if(this.disabled == state){
6567 this.disabled = state;
6570 this.el.addClass('disabled');
6574 this.el.removeClass('disabled');
6579 setActive : function(state)
6581 if(this.active == state){
6585 this.active = state;
6588 this.el.addClass('active');
6592 this.el.removeClass('active');
6597 isActive: function ()
6602 setBadge : function(str)
6608 this.badgeEl.dom.innerHTML = str;
6623 Roo.namespace('Roo.bootstrap.breadcrumb');
6627 * @class Roo.bootstrap.breadcrumb.Nav
6628 * @extends Roo.bootstrap.Component
6629 * Bootstrap Breadcrumb Nav Class
6631 * @children Roo.bootstrap.breadcrumb.Item
6634 * Create a new breadcrumb.Nav
6635 * @param {Object} config The config object
6639 Roo.bootstrap.breadcrumb.Nav = function(config){
6640 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6645 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6647 getAutoCreate : function()
6664 initEvents: function()
6666 this.olEl = this.el.select('ol',true).first();
6668 getChildContainer : function()
6684 * @class Roo.bootstrap.breadcrumb.Nav
6685 * @extends Roo.bootstrap.Component
6686 * Bootstrap Breadcrumb Nav Class
6688 * @children Roo.bootstrap.breadcrumb.Component
6689 * @cfg {String} html the content of the link.
6690 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6691 * @cfg {Boolean} active is it active
6695 * Create a new breadcrumb.Nav
6696 * @param {Object} config The config object
6699 Roo.bootstrap.breadcrumb.Item = function(config){
6700 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6705 * The img click event for the img.
6706 * @param {Roo.EventObject} e
6713 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6718 getAutoCreate : function()
6723 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6725 if (this.href !== false) {
6732 cfg.html = this.html;
6738 initEvents: function()
6741 this.el.select('a', true).first().on('click',this.onClick, this)
6745 onClick : function(e)
6748 this.fireEvent('click',this, e);
6761 * @class Roo.bootstrap.Row
6762 * @extends Roo.bootstrap.Component
6763 * Bootstrap Row class (contains columns...)
6767 * @param {Object} config The config object
6770 Roo.bootstrap.Row = function(config){
6771 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6774 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6776 getAutoCreate : function(){
6795 * @class Roo.bootstrap.Pagination
6796 * @extends Roo.bootstrap.Component
6797 * Bootstrap Pagination class
6798 * @cfg {String} size xs | sm | md | lg
6799 * @cfg {Boolean} inverse false | true
6802 * Create a new Pagination
6803 * @param {Object} config The config object
6806 Roo.bootstrap.Pagination = function(config){
6807 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6810 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6816 getAutoCreate : function(){
6822 cfg.cls += ' inverse';
6828 cfg.cls += " " + this.cls;
6846 * @class Roo.bootstrap.PaginationItem
6847 * @extends Roo.bootstrap.Component
6848 * Bootstrap PaginationItem class
6849 * @cfg {String} html text
6850 * @cfg {String} href the link
6851 * @cfg {Boolean} preventDefault (true | false) default true
6852 * @cfg {Boolean} active (true | false) default false
6853 * @cfg {Boolean} disabled default false
6857 * Create a new PaginationItem
6858 * @param {Object} config The config object
6862 Roo.bootstrap.PaginationItem = function(config){
6863 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6868 * The raw click event for the entire grid.
6869 * @param {Roo.EventObject} e
6875 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6879 preventDefault: true,
6884 getAutoCreate : function(){
6890 href : this.href ? this.href : '#',
6891 html : this.html ? this.html : ''
6901 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6905 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6911 initEvents: function() {
6913 this.el.on('click', this.onClick, this);
6916 onClick : function(e)
6918 Roo.log('PaginationItem on click ');
6919 if(this.preventDefault){
6927 this.fireEvent('click', this, e);
6943 * @class Roo.bootstrap.Slider
6944 * @extends Roo.bootstrap.Component
6945 * Bootstrap Slider class
6948 * Create a new Slider
6949 * @param {Object} config The config object
6952 Roo.bootstrap.Slider = function(config){
6953 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6956 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6958 getAutoCreate : function(){
6962 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6966 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6978 * Ext JS Library 1.1.1
6979 * Copyright(c) 2006-2007, Ext JS, LLC.
6981 * Originally Released Under LGPL - original licence link has changed is not relivant.
6984 * <script type="text/javascript">
6989 * @class Roo.grid.ColumnModel
6990 * @extends Roo.util.Observable
6991 * This is the default implementation of a ColumnModel used by the Grid. It defines
6992 * the columns in the grid.
6995 var colModel = new Roo.grid.ColumnModel([
6996 {header: "Ticker", width: 60, sortable: true, locked: true},
6997 {header: "Company Name", width: 150, sortable: true},
6998 {header: "Market Cap.", width: 100, sortable: true},
6999 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7000 {header: "Employees", width: 100, sortable: true, resizable: false}
7005 * The config options listed for this class are options which may appear in each
7006 * individual column definition.
7007 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7009 * @param {Object} config An Array of column config objects. See this class's
7010 * config objects for details.
7012 Roo.grid.ColumnModel = function(config){
7014 * The config passed into the constructor
7016 this.config = config;
7019 // if no id, create one
7020 // if the column does not have a dataIndex mapping,
7021 // map it to the order it is in the config
7022 for(var i = 0, len = config.length; i < len; i++){
7024 if(typeof c.dataIndex == "undefined"){
7027 if(typeof c.renderer == "string"){
7028 c.renderer = Roo.util.Format[c.renderer];
7030 if(typeof c.id == "undefined"){
7033 if(c.editor && c.editor.xtype){
7034 c.editor = Roo.factory(c.editor, Roo.grid);
7036 if(c.editor && c.editor.isFormField){
7037 c.editor = new Roo.grid.GridEditor(c.editor);
7039 this.lookup[c.id] = c;
7043 * The width of columns which have no width specified (defaults to 100)
7046 this.defaultWidth = 100;
7049 * Default sortable of columns which have no sortable specified (defaults to false)
7052 this.defaultSortable = false;
7056 * @event widthchange
7057 * Fires when the width of a column changes.
7058 * @param {ColumnModel} this
7059 * @param {Number} columnIndex The column index
7060 * @param {Number} newWidth The new width
7062 "widthchange": true,
7064 * @event headerchange
7065 * Fires when the text of a header changes.
7066 * @param {ColumnModel} this
7067 * @param {Number} columnIndex The column index
7068 * @param {Number} newText The new header text
7070 "headerchange": true,
7072 * @event hiddenchange
7073 * Fires when a column is hidden or "unhidden".
7074 * @param {ColumnModel} this
7075 * @param {Number} columnIndex The column index
7076 * @param {Boolean} hidden true if hidden, false otherwise
7078 "hiddenchange": true,
7080 * @event columnmoved
7081 * Fires when a column is moved.
7082 * @param {ColumnModel} this
7083 * @param {Number} oldIndex
7084 * @param {Number} newIndex
7086 "columnmoved" : true,
7088 * @event columlockchange
7089 * Fires when a column's locked state is changed
7090 * @param {ColumnModel} this
7091 * @param {Number} colIndex
7092 * @param {Boolean} locked true if locked
7094 "columnlockchange" : true
7096 Roo.grid.ColumnModel.superclass.constructor.call(this);
7098 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7100 * @cfg {String} header The header text to display in the Grid view.
7103 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7104 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7105 * specified, the column's index is used as an index into the Record's data Array.
7108 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7109 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7112 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7113 * Defaults to the value of the {@link #defaultSortable} property.
7114 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7117 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7120 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7123 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7126 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7129 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7130 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7131 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7132 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7135 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7138 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7141 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7144 * @cfg {String} cursor (Optional)
7147 * @cfg {String} tooltip (Optional)
7150 * @cfg {Number} xs (Optional)
7153 * @cfg {Number} sm (Optional)
7156 * @cfg {Number} md (Optional)
7159 * @cfg {Number} lg (Optional)
7162 * Returns the id of the column at the specified index.
7163 * @param {Number} index The column index
7164 * @return {String} the id
7166 getColumnId : function(index){
7167 return this.config[index].id;
7171 * Returns the column for a specified id.
7172 * @param {String} id The column id
7173 * @return {Object} the column
7175 getColumnById : function(id){
7176 return this.lookup[id];
7181 * Returns the column for a specified dataIndex.
7182 * @param {String} dataIndex The column dataIndex
7183 * @return {Object|Boolean} the column or false if not found
7185 getColumnByDataIndex: function(dataIndex){
7186 var index = this.findColumnIndex(dataIndex);
7187 return index > -1 ? this.config[index] : false;
7191 * Returns the index for a specified column id.
7192 * @param {String} id The column id
7193 * @return {Number} the index, or -1 if not found
7195 getIndexById : function(id){
7196 for(var i = 0, len = this.config.length; i < len; i++){
7197 if(this.config[i].id == id){
7205 * Returns the index for a specified column dataIndex.
7206 * @param {String} dataIndex The column dataIndex
7207 * @return {Number} the index, or -1 if not found
7210 findColumnIndex : function(dataIndex){
7211 for(var i = 0, len = this.config.length; i < len; i++){
7212 if(this.config[i].dataIndex == dataIndex){
7220 moveColumn : function(oldIndex, newIndex){
7221 var c = this.config[oldIndex];
7222 this.config.splice(oldIndex, 1);
7223 this.config.splice(newIndex, 0, c);
7224 this.dataMap = null;
7225 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7228 isLocked : function(colIndex){
7229 return this.config[colIndex].locked === true;
7232 setLocked : function(colIndex, value, suppressEvent){
7233 if(this.isLocked(colIndex) == value){
7236 this.config[colIndex].locked = value;
7238 this.fireEvent("columnlockchange", this, colIndex, value);
7242 getTotalLockedWidth : function(){
7244 for(var i = 0; i < this.config.length; i++){
7245 if(this.isLocked(i) && !this.isHidden(i)){
7246 this.totalWidth += this.getColumnWidth(i);
7252 getLockedCount : function(){
7253 for(var i = 0, len = this.config.length; i < len; i++){
7254 if(!this.isLocked(i)){
7259 return this.config.length;
7263 * Returns the number of columns.
7266 getColumnCount : function(visibleOnly){
7267 if(visibleOnly === true){
7269 for(var i = 0, len = this.config.length; i < len; i++){
7270 if(!this.isHidden(i)){
7276 return this.config.length;
7280 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7281 * @param {Function} fn
7282 * @param {Object} scope (optional)
7283 * @return {Array} result
7285 getColumnsBy : function(fn, scope){
7287 for(var i = 0, len = this.config.length; i < len; i++){
7288 var c = this.config[i];
7289 if(fn.call(scope||this, c, i) === true){
7297 * Returns true if the specified column is sortable.
7298 * @param {Number} col The column index
7301 isSortable : function(col){
7302 if(typeof this.config[col].sortable == "undefined"){
7303 return this.defaultSortable;
7305 return this.config[col].sortable;
7309 * Returns the rendering (formatting) function defined for the column.
7310 * @param {Number} col The column index.
7311 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7313 getRenderer : function(col){
7314 if(!this.config[col].renderer){
7315 return Roo.grid.ColumnModel.defaultRenderer;
7317 return this.config[col].renderer;
7321 * Sets the rendering (formatting) function for a column.
7322 * @param {Number} col The column index
7323 * @param {Function} fn The function to use to process the cell's raw data
7324 * to return HTML markup for the grid view. The render function is called with
7325 * the following parameters:<ul>
7326 * <li>Data value.</li>
7327 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7328 * <li>css A CSS style string to apply to the table cell.</li>
7329 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7330 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7331 * <li>Row index</li>
7332 * <li>Column index</li>
7333 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7335 setRenderer : function(col, fn){
7336 this.config[col].renderer = fn;
7340 * Returns the width for the specified column.
7341 * @param {Number} col The column index
7344 getColumnWidth : function(col){
7345 return this.config[col].width * 1 || this.defaultWidth;
7349 * Sets the width for a column.
7350 * @param {Number} col The column index
7351 * @param {Number} width The new width
7353 setColumnWidth : function(col, width, suppressEvent){
7354 this.config[col].width = width;
7355 this.totalWidth = null;
7357 this.fireEvent("widthchange", this, col, width);
7362 * Returns the total width of all columns.
7363 * @param {Boolean} includeHidden True to include hidden column widths
7366 getTotalWidth : function(includeHidden){
7367 if(!this.totalWidth){
7368 this.totalWidth = 0;
7369 for(var i = 0, len = this.config.length; i < len; i++){
7370 if(includeHidden || !this.isHidden(i)){
7371 this.totalWidth += this.getColumnWidth(i);
7375 return this.totalWidth;
7379 * Returns the header for the specified column.
7380 * @param {Number} col The column index
7383 getColumnHeader : function(col){
7384 return this.config[col].header;
7388 * Sets the header for a column.
7389 * @param {Number} col The column index
7390 * @param {String} header The new header
7392 setColumnHeader : function(col, header){
7393 this.config[col].header = header;
7394 this.fireEvent("headerchange", this, col, header);
7398 * Returns the tooltip for the specified column.
7399 * @param {Number} col The column index
7402 getColumnTooltip : function(col){
7403 return this.config[col].tooltip;
7406 * Sets the tooltip for a column.
7407 * @param {Number} col The column index
7408 * @param {String} tooltip The new tooltip
7410 setColumnTooltip : function(col, tooltip){
7411 this.config[col].tooltip = tooltip;
7415 * Returns the dataIndex for the specified column.
7416 * @param {Number} col The column index
7419 getDataIndex : function(col){
7420 return this.config[col].dataIndex;
7424 * Sets the dataIndex for a column.
7425 * @param {Number} col The column index
7426 * @param {Number} dataIndex The new dataIndex
7428 setDataIndex : function(col, dataIndex){
7429 this.config[col].dataIndex = dataIndex;
7435 * Returns true if the cell is editable.
7436 * @param {Number} colIndex The column index
7437 * @param {Number} rowIndex The row index - this is nto actually used..?
7440 isCellEditable : function(colIndex, rowIndex){
7441 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7445 * Returns the editor defined for the cell/column.
7446 * return false or null to disable editing.
7447 * @param {Number} colIndex The column index
7448 * @param {Number} rowIndex The row index
7451 getCellEditor : function(colIndex, rowIndex){
7452 return this.config[colIndex].editor;
7456 * Sets if a column is editable.
7457 * @param {Number} col The column index
7458 * @param {Boolean} editable True if the column is editable
7460 setEditable : function(col, editable){
7461 this.config[col].editable = editable;
7466 * Returns true if the column is hidden.
7467 * @param {Number} colIndex The column index
7470 isHidden : function(colIndex){
7471 return this.config[colIndex].hidden;
7476 * Returns true if the column width cannot be changed
7478 isFixed : function(colIndex){
7479 return this.config[colIndex].fixed;
7483 * Returns true if the column can be resized
7486 isResizable : function(colIndex){
7487 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7490 * Sets if a column is hidden.
7491 * @param {Number} colIndex The column index
7492 * @param {Boolean} hidden True if the column is hidden
7494 setHidden : function(colIndex, hidden){
7495 this.config[colIndex].hidden = hidden;
7496 this.totalWidth = null;
7497 this.fireEvent("hiddenchange", this, colIndex, hidden);
7501 * Sets the editor for a column.
7502 * @param {Number} col The column index
7503 * @param {Object} editor The editor object
7505 setEditor : function(col, editor){
7506 this.config[col].editor = editor;
7510 Roo.grid.ColumnModel.defaultRenderer = function(value)
7512 if(typeof value == "object") {
7515 if(typeof value == "string" && value.length < 1){
7519 return String.format("{0}", value);
7522 // Alias for backwards compatibility
7523 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7526 * Ext JS Library 1.1.1
7527 * Copyright(c) 2006-2007, Ext JS, LLC.
7529 * Originally Released Under LGPL - original licence link has changed is not relivant.
7532 * <script type="text/javascript">
7536 * @class Roo.LoadMask
7537 * A simple utility class for generically masking elements while loading data. If the element being masked has
7538 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7539 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7540 * element's UpdateManager load indicator and will be destroyed after the initial load.
7542 * Create a new LoadMask
7543 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7544 * @param {Object} config The config object
7546 Roo.LoadMask = function(el, config){
7547 this.el = Roo.get(el);
7548 Roo.apply(this, config);
7550 this.store.on('beforeload', this.onBeforeLoad, this);
7551 this.store.on('load', this.onLoad, this);
7552 this.store.on('loadexception', this.onLoadException, this);
7553 this.removeMask = false;
7555 var um = this.el.getUpdateManager();
7556 um.showLoadIndicator = false; // disable the default indicator
7557 um.on('beforeupdate', this.onBeforeLoad, this);
7558 um.on('update', this.onLoad, this);
7559 um.on('failure', this.onLoad, this);
7560 this.removeMask = true;
7564 Roo.LoadMask.prototype = {
7566 * @cfg {Boolean} removeMask
7567 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7568 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7572 * The text to display in a centered loading message box (defaults to 'Loading...')
7576 * @cfg {String} msgCls
7577 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7579 msgCls : 'x-mask-loading',
7582 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7588 * Disables the mask to prevent it from being displayed
7590 disable : function(){
7591 this.disabled = true;
7595 * Enables the mask so that it can be displayed
7597 enable : function(){
7598 this.disabled = false;
7601 onLoadException : function()
7605 if (typeof(arguments[3]) != 'undefined') {
7606 Roo.MessageBox.alert("Error loading",arguments[3]);
7610 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7611 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7618 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7623 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7627 onBeforeLoad : function(){
7629 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7634 destroy : function(){
7636 this.store.un('beforeload', this.onBeforeLoad, this);
7637 this.store.un('load', this.onLoad, this);
7638 this.store.un('loadexception', this.onLoadException, this);
7640 var um = this.el.getUpdateManager();
7641 um.un('beforeupdate', this.onBeforeLoad, this);
7642 um.un('update', this.onLoad, this);
7643 um.un('failure', this.onLoad, this);
7654 * @class Roo.bootstrap.Table
7655 * @extends Roo.bootstrap.Component
7656 * Bootstrap Table class
7657 * @cfg {String} cls table class
7658 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7659 * @cfg {String} bgcolor Specifies the background color for a table
7660 * @cfg {Number} border Specifies whether the table cells should have borders or not
7661 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7662 * @cfg {Number} cellspacing Specifies the space between cells
7663 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7664 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7665 * @cfg {String} sortable Specifies that the table should be sortable
7666 * @cfg {String} summary Specifies a summary of the content of a table
7667 * @cfg {Number} width Specifies the width of a table
7668 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7670 * @cfg {boolean} striped Should the rows be alternative striped
7671 * @cfg {boolean} bordered Add borders to the table
7672 * @cfg {boolean} hover Add hover highlighting
7673 * @cfg {boolean} condensed Format condensed
7674 * @cfg {boolean} responsive Format condensed
7675 * @cfg {Boolean} loadMask (true|false) default false
7676 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7677 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7678 * @cfg {Boolean} rowSelection (true|false) default false
7679 * @cfg {Boolean} cellSelection (true|false) default false
7680 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7681 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7682 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7683 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7687 * Create a new Table
7688 * @param {Object} config The config object
7691 Roo.bootstrap.Table = function(config){
7692 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7697 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7698 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7699 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7700 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7702 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7704 this.sm.grid = this;
7705 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7706 this.sm = this.selModel;
7707 this.sm.xmodule = this.xmodule || false;
7710 if (this.cm && typeof(this.cm.config) == 'undefined') {
7711 this.colModel = new Roo.grid.ColumnModel(this.cm);
7712 this.cm = this.colModel;
7713 this.cm.xmodule = this.xmodule || false;
7716 this.store= Roo.factory(this.store, Roo.data);
7717 this.ds = this.store;
7718 this.ds.xmodule = this.xmodule || false;
7721 if (this.footer && this.store) {
7722 this.footer.dataSource = this.ds;
7723 this.footer = Roo.factory(this.footer);
7730 * Fires when a cell is clicked
7731 * @param {Roo.bootstrap.Table} this
7732 * @param {Roo.Element} el
7733 * @param {Number} rowIndex
7734 * @param {Number} columnIndex
7735 * @param {Roo.EventObject} e
7739 * @event celldblclick
7740 * Fires when a cell is double clicked
7741 * @param {Roo.bootstrap.Table} this
7742 * @param {Roo.Element} el
7743 * @param {Number} rowIndex
7744 * @param {Number} columnIndex
7745 * @param {Roo.EventObject} e
7747 "celldblclick" : true,
7750 * Fires when a row is clicked
7751 * @param {Roo.bootstrap.Table} this
7752 * @param {Roo.Element} el
7753 * @param {Number} rowIndex
7754 * @param {Roo.EventObject} e
7758 * @event rowdblclick
7759 * Fires when a row is double clicked
7760 * @param {Roo.bootstrap.Table} this
7761 * @param {Roo.Element} el
7762 * @param {Number} rowIndex
7763 * @param {Roo.EventObject} e
7765 "rowdblclick" : true,
7768 * Fires when a mouseover occur
7769 * @param {Roo.bootstrap.Table} this
7770 * @param {Roo.Element} el
7771 * @param {Number} rowIndex
7772 * @param {Number} columnIndex
7773 * @param {Roo.EventObject} e
7778 * Fires when a mouseout occur
7779 * @param {Roo.bootstrap.Table} this
7780 * @param {Roo.Element} el
7781 * @param {Number} rowIndex
7782 * @param {Number} columnIndex
7783 * @param {Roo.EventObject} e
7788 * Fires when a row is rendered, so you can change add a style to it.
7789 * @param {Roo.bootstrap.Table} this
7790 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7794 * @event rowsrendered
7795 * Fires when all the rows have been rendered
7796 * @param {Roo.bootstrap.Table} this
7798 'rowsrendered' : true,
7800 * @event contextmenu
7801 * The raw contextmenu event for the entire grid.
7802 * @param {Roo.EventObject} e
7804 "contextmenu" : true,
7806 * @event rowcontextmenu
7807 * Fires when a row is right clicked
7808 * @param {Roo.bootstrap.Table} this
7809 * @param {Number} rowIndex
7810 * @param {Roo.EventObject} e
7812 "rowcontextmenu" : true,
7814 * @event cellcontextmenu
7815 * Fires when a cell is right clicked
7816 * @param {Roo.bootstrap.Table} this
7817 * @param {Number} rowIndex
7818 * @param {Number} cellIndex
7819 * @param {Roo.EventObject} e
7821 "cellcontextmenu" : true,
7823 * @event headercontextmenu
7824 * Fires when a header is right clicked
7825 * @param {Roo.bootstrap.Table} this
7826 * @param {Number} columnIndex
7827 * @param {Roo.EventObject} e
7829 "headercontextmenu" : true
7833 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7859 rowSelection : false,
7860 cellSelection : false,
7863 // Roo.Element - the tbody
7865 // Roo.Element - thead element
7868 container: false, // used by gridpanel...
7874 auto_hide_footer : false,
7876 getAutoCreate : function()
7878 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7885 if (this.scrollBody) {
7886 cfg.cls += ' table-body-fixed';
7889 cfg.cls += ' table-striped';
7893 cfg.cls += ' table-hover';
7895 if (this.bordered) {
7896 cfg.cls += ' table-bordered';
7898 if (this.condensed) {
7899 cfg.cls += ' table-condensed';
7901 if (this.responsive) {
7902 cfg.cls += ' table-responsive';
7906 cfg.cls+= ' ' +this.cls;
7909 // this lot should be simplifed...
7922 ].forEach(function(k) {
7930 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7933 if(this.store || this.cm){
7934 if(this.headerShow){
7935 cfg.cn.push(this.renderHeader());
7938 cfg.cn.push(this.renderBody());
7940 if(this.footerShow){
7941 cfg.cn.push(this.renderFooter());
7943 // where does this come from?
7944 //cfg.cls+= ' TableGrid';
7947 return { cn : [ cfg ] };
7950 initEvents : function()
7952 if(!this.store || !this.cm){
7955 if (this.selModel) {
7956 this.selModel.initEvents();
7960 //Roo.log('initEvents with ds!!!!');
7962 this.mainBody = this.el.select('tbody', true).first();
7963 this.mainHead = this.el.select('thead', true).first();
7964 this.mainFoot = this.el.select('tfoot', true).first();
7970 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7971 e.on('click', _this.sort, _this);
7974 this.mainBody.on("click", this.onClick, this);
7975 this.mainBody.on("dblclick", this.onDblClick, this);
7977 // why is this done????? = it breaks dialogs??
7978 //this.parent().el.setStyle('position', 'relative');
7982 this.footer.parentId = this.id;
7983 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7986 this.el.select('tfoot tr td').first().addClass('hide');
7991 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7994 this.store.on('load', this.onLoad, this);
7995 this.store.on('beforeload', this.onBeforeLoad, this);
7996 this.store.on('update', this.onUpdate, this);
7997 this.store.on('add', this.onAdd, this);
7998 this.store.on("clear", this.clear, this);
8000 this.el.on("contextmenu", this.onContextMenu, this);
8002 this.mainBody.on('scroll', this.onBodyScroll, this);
8004 this.cm.on("headerchange", this.onHeaderChange, this);
8006 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8010 onContextMenu : function(e, t)
8012 this.processEvent("contextmenu", e);
8015 processEvent : function(name, e)
8017 if (name != 'touchstart' ) {
8018 this.fireEvent(name, e);
8021 var t = e.getTarget();
8023 var cell = Roo.get(t);
8029 if(cell.findParent('tfoot', false, true)){
8033 if(cell.findParent('thead', false, true)){
8035 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8036 cell = Roo.get(t).findParent('th', false, true);
8038 Roo.log("failed to find th in thead?");
8039 Roo.log(e.getTarget());
8044 var cellIndex = cell.dom.cellIndex;
8046 var ename = name == 'touchstart' ? 'click' : name;
8047 this.fireEvent("header" + ename, this, cellIndex, e);
8052 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8053 cell = Roo.get(t).findParent('td', false, true);
8055 Roo.log("failed to find th in tbody?");
8056 Roo.log(e.getTarget());
8061 var row = cell.findParent('tr', false, true);
8062 var cellIndex = cell.dom.cellIndex;
8063 var rowIndex = row.dom.rowIndex - 1;
8067 this.fireEvent("row" + name, this, rowIndex, e);
8071 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8077 onMouseover : function(e, el)
8079 var cell = Roo.get(el);
8085 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8086 cell = cell.findParent('td', false, true);
8089 var row = cell.findParent('tr', false, true);
8090 var cellIndex = cell.dom.cellIndex;
8091 var rowIndex = row.dom.rowIndex - 1; // start from 0
8093 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8097 onMouseout : function(e, el)
8099 var cell = Roo.get(el);
8105 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8106 cell = cell.findParent('td', false, true);
8109 var row = cell.findParent('tr', false, true);
8110 var cellIndex = cell.dom.cellIndex;
8111 var rowIndex = row.dom.rowIndex - 1; // start from 0
8113 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8117 onClick : function(e, el)
8119 var cell = Roo.get(el);
8121 if(!cell || (!this.cellSelection && !this.rowSelection)){
8125 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8126 cell = cell.findParent('td', false, true);
8129 if(!cell || typeof(cell) == 'undefined'){
8133 var row = cell.findParent('tr', false, true);
8135 if(!row || typeof(row) == 'undefined'){
8139 var cellIndex = cell.dom.cellIndex;
8140 var rowIndex = this.getRowIndex(row);
8142 // why??? - should these not be based on SelectionModel?
8143 if(this.cellSelection){
8144 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8147 if(this.rowSelection){
8148 this.fireEvent('rowclick', this, row, rowIndex, e);
8154 onDblClick : function(e,el)
8156 var cell = Roo.get(el);
8158 if(!cell || (!this.cellSelection && !this.rowSelection)){
8162 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8163 cell = cell.findParent('td', false, true);
8166 if(!cell || typeof(cell) == 'undefined'){
8170 var row = cell.findParent('tr', false, true);
8172 if(!row || typeof(row) == 'undefined'){
8176 var cellIndex = cell.dom.cellIndex;
8177 var rowIndex = this.getRowIndex(row);
8179 if(this.cellSelection){
8180 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8183 if(this.rowSelection){
8184 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8188 sort : function(e,el)
8190 var col = Roo.get(el);
8192 if(!col.hasClass('sortable')){
8196 var sort = col.attr('sort');
8199 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8203 this.store.sortInfo = {field : sort, direction : dir};
8206 Roo.log("calling footer first");
8207 this.footer.onClick('first');
8210 this.store.load({ params : { start : 0 } });
8214 renderHeader : function()
8222 this.totalWidth = 0;
8224 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8226 var config = cm.config[i];
8230 cls : 'x-hcol-' + i,
8232 html: cm.getColumnHeader(i)
8237 if(typeof(config.sortable) != 'undefined' && config.sortable){
8239 c.html = '<i class="glyphicon"></i>' + c.html;
8242 // could use BS4 hidden-..-down
8244 if(typeof(config.lgHeader) != 'undefined'){
8245 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8248 if(typeof(config.mdHeader) != 'undefined'){
8249 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8252 if(typeof(config.smHeader) != 'undefined'){
8253 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8256 if(typeof(config.xsHeader) != 'undefined'){
8257 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8264 if(typeof(config.tooltip) != 'undefined'){
8265 c.tooltip = config.tooltip;
8268 if(typeof(config.colspan) != 'undefined'){
8269 c.colspan = config.colspan;
8272 if(typeof(config.hidden) != 'undefined' && config.hidden){
8273 c.style += ' display:none;';
8276 if(typeof(config.dataIndex) != 'undefined'){
8277 c.sort = config.dataIndex;
8282 if(typeof(config.align) != 'undefined' && config.align.length){
8283 c.style += ' text-align:' + config.align + ';';
8286 if(typeof(config.width) != 'undefined'){
8287 c.style += ' width:' + config.width + 'px;';
8288 this.totalWidth += config.width;
8290 this.totalWidth += 100; // assume minimum of 100 per column?
8293 if(typeof(config.cls) != 'undefined'){
8294 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8297 ['xs','sm','md','lg'].map(function(size){
8299 if(typeof(config[size]) == 'undefined'){
8303 if (!config[size]) { // 0 = hidden
8304 // BS 4 '0' is treated as hide that column and below.
8305 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8309 c.cls += ' col-' + size + '-' + config[size] + (
8310 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8322 renderBody : function()
8332 colspan : this.cm.getColumnCount()
8342 renderFooter : function()
8352 colspan : this.cm.getColumnCount()
8366 // Roo.log('ds onload');
8371 var ds = this.store;
8373 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8374 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8375 if (_this.store.sortInfo) {
8377 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8378 e.select('i', true).addClass(['glyphicon-arrow-up']);
8381 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8382 e.select('i', true).addClass(['glyphicon-arrow-down']);
8387 var tbody = this.mainBody;
8389 if(ds.getCount() > 0){
8390 ds.data.each(function(d,rowIndex){
8391 var row = this.renderRow(cm, ds, rowIndex);
8393 tbody.createChild(row);
8397 if(row.cellObjects.length){
8398 Roo.each(row.cellObjects, function(r){
8399 _this.renderCellObject(r);
8406 var tfoot = this.el.select('tfoot', true).first();
8408 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8410 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8412 var total = this.ds.getTotalCount();
8414 if(this.footer.pageSize < total){
8415 this.mainFoot.show();
8419 Roo.each(this.el.select('tbody td', true).elements, function(e){
8420 e.on('mouseover', _this.onMouseover, _this);
8423 Roo.each(this.el.select('tbody td', true).elements, function(e){
8424 e.on('mouseout', _this.onMouseout, _this);
8426 this.fireEvent('rowsrendered', this);
8432 onUpdate : function(ds,record)
8434 this.refreshRow(record);
8438 onRemove : function(ds, record, index, isUpdate){
8439 if(isUpdate !== true){
8440 this.fireEvent("beforerowremoved", this, index, record);
8442 var bt = this.mainBody.dom;
8444 var rows = this.el.select('tbody > tr', true).elements;
8446 if(typeof(rows[index]) != 'undefined'){
8447 bt.removeChild(rows[index].dom);
8450 // if(bt.rows[index]){
8451 // bt.removeChild(bt.rows[index]);
8454 if(isUpdate !== true){
8455 //this.stripeRows(index);
8456 //this.syncRowHeights(index, index);
8458 this.fireEvent("rowremoved", this, index, record);
8462 onAdd : function(ds, records, rowIndex)
8464 //Roo.log('on Add called');
8465 // - note this does not handle multiple adding very well..
8466 var bt = this.mainBody.dom;
8467 for (var i =0 ; i < records.length;i++) {
8468 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8469 //Roo.log(records[i]);
8470 //Roo.log(this.store.getAt(rowIndex+i));
8471 this.insertRow(this.store, rowIndex + i, false);
8478 refreshRow : function(record){
8479 var ds = this.store, index;
8480 if(typeof record == 'number'){
8482 record = ds.getAt(index);
8484 index = ds.indexOf(record);
8486 return; // should not happen - but seems to
8489 this.insertRow(ds, index, true);
8491 this.onRemove(ds, record, index+1, true);
8493 //this.syncRowHeights(index, index);
8495 this.fireEvent("rowupdated", this, index, record);
8498 insertRow : function(dm, rowIndex, isUpdate){
8501 this.fireEvent("beforerowsinserted", this, rowIndex);
8503 //var s = this.getScrollState();
8504 var row = this.renderRow(this.cm, this.store, rowIndex);
8505 // insert before rowIndex..
8506 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8510 if(row.cellObjects.length){
8511 Roo.each(row.cellObjects, function(r){
8512 _this.renderCellObject(r);
8517 this.fireEvent("rowsinserted", this, rowIndex);
8518 //this.syncRowHeights(firstRow, lastRow);
8519 //this.stripeRows(firstRow);
8526 getRowDom : function(rowIndex)
8528 var rows = this.el.select('tbody > tr', true).elements;
8530 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8533 // returns the object tree for a tr..
8536 renderRow : function(cm, ds, rowIndex)
8538 var d = ds.getAt(rowIndex);
8542 cls : 'x-row-' + rowIndex,
8546 var cellObjects = [];
8548 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8549 var config = cm.config[i];
8551 var renderer = cm.getRenderer(i);
8555 if(typeof(renderer) !== 'undefined'){
8556 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8558 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8559 // and are rendered into the cells after the row is rendered - using the id for the element.
8561 if(typeof(value) === 'object'){
8571 rowIndex : rowIndex,
8576 this.fireEvent('rowclass', this, rowcfg);
8580 cls : rowcfg.rowClass + ' x-col-' + i,
8582 html: (typeof(value) === 'object') ? '' : value
8589 if(typeof(config.colspan) != 'undefined'){
8590 td.colspan = config.colspan;
8593 if(typeof(config.hidden) != 'undefined' && config.hidden){
8594 td.style += ' display:none;';
8597 if(typeof(config.align) != 'undefined' && config.align.length){
8598 td.style += ' text-align:' + config.align + ';';
8600 if(typeof(config.valign) != 'undefined' && config.valign.length){
8601 td.style += ' vertical-align:' + config.valign + ';';
8604 if(typeof(config.width) != 'undefined'){
8605 td.style += ' width:' + config.width + 'px;';
8608 if(typeof(config.cursor) != 'undefined'){
8609 td.style += ' cursor:' + config.cursor + ';';
8612 if(typeof(config.cls) != 'undefined'){
8613 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8616 ['xs','sm','md','lg'].map(function(size){
8618 if(typeof(config[size]) == 'undefined'){
8624 if (!config[size]) { // 0 = hidden
8625 // BS 4 '0' is treated as hide that column and below.
8626 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8630 td.cls += ' col-' + size + '-' + config[size] + (
8631 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8641 row.cellObjects = cellObjects;
8649 onBeforeLoad : function()
8658 this.el.select('tbody', true).first().dom.innerHTML = '';
8661 * Show or hide a row.
8662 * @param {Number} rowIndex to show or hide
8663 * @param {Boolean} state hide
8665 setRowVisibility : function(rowIndex, state)
8667 var bt = this.mainBody.dom;
8669 var rows = this.el.select('tbody > tr', true).elements;
8671 if(typeof(rows[rowIndex]) == 'undefined'){
8674 rows[rowIndex].dom.style.display = state ? '' : 'none';
8678 getSelectionModel : function(){
8680 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8682 return this.selModel;
8685 * Render the Roo.bootstrap object from renderder
8687 renderCellObject : function(r)
8691 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8693 var t = r.cfg.render(r.container);
8696 Roo.each(r.cfg.cn, function(c){
8698 container: t.getChildContainer(),
8701 _this.renderCellObject(child);
8706 getRowIndex : function(row)
8710 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8721 * Returns the grid's underlying element = used by panel.Grid
8722 * @return {Element} The element
8724 getGridEl : function(){
8728 * Forces a resize - used by panel.Grid
8729 * @return {Element} The element
8731 autoSize : function()
8733 //var ctr = Roo.get(this.container.dom.parentElement);
8734 var ctr = Roo.get(this.el.dom);
8736 var thd = this.getGridEl().select('thead',true).first();
8737 var tbd = this.getGridEl().select('tbody', true).first();
8738 var tfd = this.getGridEl().select('tfoot', true).first();
8740 var cw = ctr.getWidth();
8741 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8745 tbd.setWidth(ctr.getWidth());
8746 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8747 // this needs fixing for various usage - currently only hydra job advers I think..
8749 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8751 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8754 cw = Math.max(cw, this.totalWidth);
8755 this.getGridEl().select('tbody tr',true).setWidth(cw);
8757 // resize 'expandable coloumn?
8759 return; // we doe not have a view in this design..
8762 onBodyScroll: function()
8764 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8766 this.mainHead.setStyle({
8767 'position' : 'relative',
8768 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8774 var scrollHeight = this.mainBody.dom.scrollHeight;
8776 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8778 var height = this.mainBody.getHeight();
8780 if(scrollHeight - height == scrollTop) {
8782 var total = this.ds.getTotalCount();
8784 if(this.footer.cursor + this.footer.pageSize < total){
8786 this.footer.ds.load({
8788 start : this.footer.cursor + this.footer.pageSize,
8789 limit : this.footer.pageSize
8799 onHeaderChange : function()
8801 var header = this.renderHeader();
8802 var table = this.el.select('table', true).first();
8804 this.mainHead.remove();
8805 this.mainHead = table.createChild(header, this.mainBody, false);
8808 onHiddenChange : function(colModel, colIndex, hidden)
8810 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8811 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8813 this.CSS.updateRule(thSelector, "display", "");
8814 this.CSS.updateRule(tdSelector, "display", "");
8817 this.CSS.updateRule(thSelector, "display", "none");
8818 this.CSS.updateRule(tdSelector, "display", "none");
8821 this.onHeaderChange();
8825 setColumnWidth: function(col_index, width)
8827 // width = "md-2 xs-2..."
8828 if(!this.colModel.config[col_index]) {
8832 var w = width.split(" ");
8834 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8836 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8839 for(var j = 0; j < w.length; j++) {
8845 var size_cls = w[j].split("-");
8847 if(!Number.isInteger(size_cls[1] * 1)) {
8851 if(!this.colModel.config[col_index][size_cls[0]]) {
8855 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8859 h_row[0].classList.replace(
8860 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8861 "col-"+size_cls[0]+"-"+size_cls[1]
8864 for(var i = 0; i < rows.length; i++) {
8866 var size_cls = w[j].split("-");
8868 if(!Number.isInteger(size_cls[1] * 1)) {
8872 if(!this.colModel.config[col_index][size_cls[0]]) {
8876 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8880 rows[i].classList.replace(
8881 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8882 "col-"+size_cls[0]+"-"+size_cls[1]
8886 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8901 * @class Roo.bootstrap.TableCell
8902 * @extends Roo.bootstrap.Component
8903 * Bootstrap TableCell class
8904 * @cfg {String} html cell contain text
8905 * @cfg {String} cls cell class
8906 * @cfg {String} tag cell tag (td|th) default td
8907 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8908 * @cfg {String} align Aligns the content in a cell
8909 * @cfg {String} axis Categorizes cells
8910 * @cfg {String} bgcolor Specifies the background color of a cell
8911 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8912 * @cfg {Number} colspan Specifies the number of columns a cell should span
8913 * @cfg {String} headers Specifies one or more header cells a cell is related to
8914 * @cfg {Number} height Sets the height of a cell
8915 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8916 * @cfg {Number} rowspan Sets the number of rows a cell should span
8917 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8918 * @cfg {String} valign Vertical aligns the content in a cell
8919 * @cfg {Number} width Specifies the width of a cell
8922 * Create a new TableCell
8923 * @param {Object} config The config object
8926 Roo.bootstrap.TableCell = function(config){
8927 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8930 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8950 getAutoCreate : function(){
8951 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8971 cfg.align=this.align
8977 cfg.bgcolor=this.bgcolor
8980 cfg.charoff=this.charoff
8983 cfg.colspan=this.colspan
8986 cfg.headers=this.headers
8989 cfg.height=this.height
8992 cfg.nowrap=this.nowrap
8995 cfg.rowspan=this.rowspan
8998 cfg.scope=this.scope
9001 cfg.valign=this.valign
9004 cfg.width=this.width
9023 * @class Roo.bootstrap.TableRow
9024 * @extends Roo.bootstrap.Component
9025 * Bootstrap TableRow class
9026 * @cfg {String} cls row class
9027 * @cfg {String} align Aligns the content in a table row
9028 * @cfg {String} bgcolor Specifies a background color for a table row
9029 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9030 * @cfg {String} valign Vertical aligns the content in a table row
9033 * Create a new TableRow
9034 * @param {Object} config The config object
9037 Roo.bootstrap.TableRow = function(config){
9038 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9041 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9049 getAutoCreate : function(){
9050 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9060 cfg.align = this.align;
9063 cfg.bgcolor = this.bgcolor;
9066 cfg.charoff = this.charoff;
9069 cfg.valign = this.valign;
9087 * @class Roo.bootstrap.TableBody
9088 * @extends Roo.bootstrap.Component
9089 * Bootstrap TableBody class
9090 * @cfg {String} cls element class
9091 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9092 * @cfg {String} align Aligns the content inside the element
9093 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9094 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9097 * Create a new TableBody
9098 * @param {Object} config The config object
9101 Roo.bootstrap.TableBody = function(config){
9102 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9105 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9113 getAutoCreate : function(){
9114 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9128 cfg.align = this.align;
9131 cfg.charoff = this.charoff;
9134 cfg.valign = this.valign;
9141 // initEvents : function()
9148 // this.store = Roo.factory(this.store, Roo.data);
9149 // this.store.on('load', this.onLoad, this);
9151 // this.store.load();
9155 // onLoad: function ()
9157 // this.fireEvent('load', this);
9167 * Ext JS Library 1.1.1
9168 * Copyright(c) 2006-2007, Ext JS, LLC.
9170 * Originally Released Under LGPL - original licence link has changed is not relivant.
9173 * <script type="text/javascript">
9176 // as we use this in bootstrap.
9177 Roo.namespace('Roo.form');
9179 * @class Roo.form.Action
9180 * Internal Class used to handle form actions
9182 * @param {Roo.form.BasicForm} el The form element or its id
9183 * @param {Object} config Configuration options
9188 // define the action interface
9189 Roo.form.Action = function(form, options){
9191 this.options = options || {};
9194 * Client Validation Failed
9197 Roo.form.Action.CLIENT_INVALID = 'client';
9199 * Server Validation Failed
9202 Roo.form.Action.SERVER_INVALID = 'server';
9204 * Connect to Server Failed
9207 Roo.form.Action.CONNECT_FAILURE = 'connect';
9209 * Reading Data from Server Failed
9212 Roo.form.Action.LOAD_FAILURE = 'load';
9214 Roo.form.Action.prototype = {
9216 failureType : undefined,
9217 response : undefined,
9221 run : function(options){
9226 success : function(response){
9231 handleResponse : function(response){
9235 // default connection failure
9236 failure : function(response){
9238 this.response = response;
9239 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9240 this.form.afterAction(this, false);
9243 processResponse : function(response){
9244 this.response = response;
9245 if(!response.responseText){
9248 this.result = this.handleResponse(response);
9252 // utility functions used internally
9253 getUrl : function(appendParams){
9254 var url = this.options.url || this.form.url || this.form.el.dom.action;
9256 var p = this.getParams();
9258 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9264 getMethod : function(){
9265 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9268 getParams : function(){
9269 var bp = this.form.baseParams;
9270 var p = this.options.params;
9272 if(typeof p == "object"){
9273 p = Roo.urlEncode(Roo.applyIf(p, bp));
9274 }else if(typeof p == 'string' && bp){
9275 p += '&' + Roo.urlEncode(bp);
9278 p = Roo.urlEncode(bp);
9283 createCallback : function(){
9285 success: this.success,
9286 failure: this.failure,
9288 timeout: (this.form.timeout*1000),
9289 upload: this.form.fileUpload ? this.success : undefined
9294 Roo.form.Action.Submit = function(form, options){
9295 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9298 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9301 haveProgress : false,
9302 uploadComplete : false,
9304 // uploadProgress indicator.
9305 uploadProgress : function()
9307 if (!this.form.progressUrl) {
9311 if (!this.haveProgress) {
9312 Roo.MessageBox.progress("Uploading", "Uploading");
9314 if (this.uploadComplete) {
9315 Roo.MessageBox.hide();
9319 this.haveProgress = true;
9321 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9323 var c = new Roo.data.Connection();
9325 url : this.form.progressUrl,
9330 success : function(req){
9331 //console.log(data);
9335 rdata = Roo.decode(req.responseText)
9337 Roo.log("Invalid data from server..");
9341 if (!rdata || !rdata.success) {
9343 Roo.MessageBox.alert(Roo.encode(rdata));
9346 var data = rdata.data;
9348 if (this.uploadComplete) {
9349 Roo.MessageBox.hide();
9354 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9355 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9358 this.uploadProgress.defer(2000,this);
9361 failure: function(data) {
9362 Roo.log('progress url failed ');
9373 // run get Values on the form, so it syncs any secondary forms.
9374 this.form.getValues();
9376 var o = this.options;
9377 var method = this.getMethod();
9378 var isPost = method == 'POST';
9379 if(o.clientValidation === false || this.form.isValid()){
9381 if (this.form.progressUrl) {
9382 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9383 (new Date() * 1) + '' + Math.random());
9388 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9389 form:this.form.el.dom,
9390 url:this.getUrl(!isPost),
9392 params:isPost ? this.getParams() : null,
9393 isUpload: this.form.fileUpload,
9394 formData : this.form.formData
9397 this.uploadProgress();
9399 }else if (o.clientValidation !== false){ // client validation failed
9400 this.failureType = Roo.form.Action.CLIENT_INVALID;
9401 this.form.afterAction(this, false);
9405 success : function(response)
9407 this.uploadComplete= true;
9408 if (this.haveProgress) {
9409 Roo.MessageBox.hide();
9413 var result = this.processResponse(response);
9414 if(result === true || result.success){
9415 this.form.afterAction(this, true);
9419 this.form.markInvalid(result.errors);
9420 this.failureType = Roo.form.Action.SERVER_INVALID;
9422 this.form.afterAction(this, false);
9424 failure : function(response)
9426 this.uploadComplete= true;
9427 if (this.haveProgress) {
9428 Roo.MessageBox.hide();
9431 this.response = response;
9432 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9433 this.form.afterAction(this, false);
9436 handleResponse : function(response){
9437 if(this.form.errorReader){
9438 var rs = this.form.errorReader.read(response);
9441 for(var i = 0, len = rs.records.length; i < len; i++) {
9442 var r = rs.records[i];
9446 if(errors.length < 1){
9450 success : rs.success,
9456 ret = Roo.decode(response.responseText);
9460 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9470 Roo.form.Action.Load = function(form, options){
9471 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9472 this.reader = this.form.reader;
9475 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9480 Roo.Ajax.request(Roo.apply(
9481 this.createCallback(), {
9482 method:this.getMethod(),
9483 url:this.getUrl(false),
9484 params:this.getParams()
9488 success : function(response){
9490 var result = this.processResponse(response);
9491 if(result === true || !result.success || !result.data){
9492 this.failureType = Roo.form.Action.LOAD_FAILURE;
9493 this.form.afterAction(this, false);
9496 this.form.clearInvalid();
9497 this.form.setValues(result.data);
9498 this.form.afterAction(this, true);
9501 handleResponse : function(response){
9502 if(this.form.reader){
9503 var rs = this.form.reader.read(response);
9504 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9506 success : rs.success,
9510 return Roo.decode(response.responseText);
9514 Roo.form.Action.ACTION_TYPES = {
9515 'load' : Roo.form.Action.Load,
9516 'submit' : Roo.form.Action.Submit
9525 * @class Roo.bootstrap.Form
9526 * @extends Roo.bootstrap.Component
9527 * Bootstrap Form class
9528 * @cfg {String} method GET | POST (default POST)
9529 * @cfg {String} labelAlign top | left (default top)
9530 * @cfg {String} align left | right - for navbars
9531 * @cfg {Boolean} loadMask load mask when submit (default true)
9536 * @param {Object} config The config object
9540 Roo.bootstrap.Form = function(config){
9542 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9544 Roo.bootstrap.Form.popover.apply();
9548 * @event clientvalidation
9549 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9550 * @param {Form} this
9551 * @param {Boolean} valid true if the form has passed client-side validation
9553 clientvalidation: true,
9555 * @event beforeaction
9556 * Fires before any action is performed. Return false to cancel the action.
9557 * @param {Form} this
9558 * @param {Action} action The action to be performed
9562 * @event actionfailed
9563 * Fires when an action fails.
9564 * @param {Form} this
9565 * @param {Action} action The action that failed
9567 actionfailed : true,
9569 * @event actioncomplete
9570 * Fires when an action is completed.
9571 * @param {Form} this
9572 * @param {Action} action The action that completed
9574 actioncomplete : true
9578 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9581 * @cfg {String} method
9582 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9587 * The URL to use for form actions if one isn't supplied in the action options.
9590 * @cfg {Boolean} fileUpload
9591 * Set to true if this form is a file upload.
9595 * @cfg {Object} baseParams
9596 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9600 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9604 * @cfg {Sting} align (left|right) for navbar forms
9609 activeAction : null,
9612 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9613 * element by passing it or its id or mask the form itself by passing in true.
9616 waitMsgTarget : false,
9621 * @cfg {Boolean} errorMask (true|false) default false
9626 * @cfg {Number} maskOffset Default 100
9631 * @cfg {Boolean} maskBody
9635 getAutoCreate : function(){
9639 method : this.method || 'POST',
9640 id : this.id || Roo.id(),
9643 if (this.parent().xtype.match(/^Nav/)) {
9644 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9648 if (this.labelAlign == 'left' ) {
9649 cfg.cls += ' form-horizontal';
9655 initEvents : function()
9657 this.el.on('submit', this.onSubmit, this);
9658 // this was added as random key presses on the form where triggering form submit.
9659 this.el.on('keypress', function(e) {
9660 if (e.getCharCode() != 13) {
9663 // we might need to allow it for textareas.. and some other items.
9664 // check e.getTarget().
9666 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9670 Roo.log("keypress blocked");
9678 onSubmit : function(e){
9683 * Returns true if client-side validation on the form is successful.
9686 isValid : function(){
9687 var items = this.getItems();
9691 items.each(function(f){
9697 Roo.log('invalid field: ' + f.name);
9701 if(!target && f.el.isVisible(true)){
9707 if(this.errorMask && !valid){
9708 Roo.bootstrap.Form.popover.mask(this, target);
9715 * Returns true if any fields in this form have changed since their original load.
9718 isDirty : function(){
9720 var items = this.getItems();
9721 items.each(function(f){
9731 * Performs a predefined action (submit or load) or custom actions you define on this form.
9732 * @param {String} actionName The name of the action type
9733 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9734 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9735 * accept other config options):
9737 Property Type Description
9738 ---------------- --------------- ----------------------------------------------------------------------------------
9739 url String The url for the action (defaults to the form's url)
9740 method String The form method to use (defaults to the form's method, or POST if not defined)
9741 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9742 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9743 validate the form on the client (defaults to false)
9745 * @return {BasicForm} this
9747 doAction : function(action, options){
9748 if(typeof action == 'string'){
9749 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9751 if(this.fireEvent('beforeaction', this, action) !== false){
9752 this.beforeAction(action);
9753 action.run.defer(100, action);
9759 beforeAction : function(action){
9760 var o = action.options;
9765 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9767 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9770 // not really supported yet.. ??
9772 //if(this.waitMsgTarget === true){
9773 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9774 //}else if(this.waitMsgTarget){
9775 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9776 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9778 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9784 afterAction : function(action, success){
9785 this.activeAction = null;
9786 var o = action.options;
9791 Roo.get(document.body).unmask();
9797 //if(this.waitMsgTarget === true){
9798 // this.el.unmask();
9799 //}else if(this.waitMsgTarget){
9800 // this.waitMsgTarget.unmask();
9802 // Roo.MessageBox.updateProgress(1);
9803 // Roo.MessageBox.hide();
9810 Roo.callback(o.success, o.scope, [this, action]);
9811 this.fireEvent('actioncomplete', this, action);
9815 // failure condition..
9816 // we have a scenario where updates need confirming.
9817 // eg. if a locking scenario exists..
9818 // we look for { errors : { needs_confirm : true }} in the response.
9820 (typeof(action.result) != 'undefined') &&
9821 (typeof(action.result.errors) != 'undefined') &&
9822 (typeof(action.result.errors.needs_confirm) != 'undefined')
9825 Roo.log("not supported yet");
9828 Roo.MessageBox.confirm(
9829 "Change requires confirmation",
9830 action.result.errorMsg,
9835 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9845 Roo.callback(o.failure, o.scope, [this, action]);
9846 // show an error message if no failed handler is set..
9847 if (!this.hasListener('actionfailed')) {
9848 Roo.log("need to add dialog support");
9850 Roo.MessageBox.alert("Error",
9851 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9852 action.result.errorMsg :
9853 "Saving Failed, please check your entries or try again"
9858 this.fireEvent('actionfailed', this, action);
9863 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9864 * @param {String} id The value to search for
9867 findField : function(id){
9868 var items = this.getItems();
9869 var field = items.get(id);
9871 items.each(function(f){
9872 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9879 return field || null;
9882 * Mark fields in this form invalid in bulk.
9883 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9884 * @return {BasicForm} this
9886 markInvalid : function(errors){
9887 if(errors instanceof Array){
9888 for(var i = 0, len = errors.length; i < len; i++){
9889 var fieldError = errors[i];
9890 var f = this.findField(fieldError.id);
9892 f.markInvalid(fieldError.msg);
9898 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9899 field.markInvalid(errors[id]);
9903 //Roo.each(this.childForms || [], function (f) {
9904 // f.markInvalid(errors);
9911 * Set values for fields in this form in bulk.
9912 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9913 * @return {BasicForm} this
9915 setValues : function(values){
9916 if(values instanceof Array){ // array of objects
9917 for(var i = 0, len = values.length; i < len; i++){
9919 var f = this.findField(v.id);
9921 f.setValue(v.value);
9922 if(this.trackResetOnLoad){
9923 f.originalValue = f.getValue();
9927 }else{ // object hash
9930 if(typeof values[id] != 'function' && (field = this.findField(id))){
9932 if (field.setFromData &&
9934 field.displayField &&
9935 // combos' with local stores can
9936 // be queried via setValue()
9937 // to set their value..
9938 (field.store && !field.store.isLocal)
9942 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9943 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9944 field.setFromData(sd);
9946 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9948 field.setFromData(values);
9951 field.setValue(values[id]);
9955 if(this.trackResetOnLoad){
9956 field.originalValue = field.getValue();
9962 //Roo.each(this.childForms || [], function (f) {
9963 // f.setValues(values);
9970 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9971 * they are returned as an array.
9972 * @param {Boolean} asString
9975 getValues : function(asString){
9976 //if (this.childForms) {
9977 // copy values from the child forms
9978 // Roo.each(this.childForms, function (f) {
9979 // this.setValues(f.getValues());
9985 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9986 if(asString === true){
9989 return Roo.urlDecode(fs);
9993 * Returns the fields in this form as an object with key/value pairs.
9994 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9997 getFieldValues : function(with_hidden)
9999 var items = this.getItems();
10001 items.each(function(f){
10003 if (!f.getName()) {
10007 var v = f.getValue();
10009 if (f.inputType =='radio') {
10010 if (typeof(ret[f.getName()]) == 'undefined') {
10011 ret[f.getName()] = ''; // empty..
10014 if (!f.el.dom.checked) {
10018 v = f.el.dom.value;
10022 if(f.xtype == 'MoneyField'){
10023 ret[f.currencyName] = f.getCurrency();
10026 // not sure if this supported any more..
10027 if ((typeof(v) == 'object') && f.getRawValue) {
10028 v = f.getRawValue() ; // dates..
10030 // combo boxes where name != hiddenName...
10031 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10032 ret[f.name] = f.getRawValue();
10034 ret[f.getName()] = v;
10041 * Clears all invalid messages in this form.
10042 * @return {BasicForm} this
10044 clearInvalid : function(){
10045 var items = this.getItems();
10047 items.each(function(f){
10055 * Resets this form.
10056 * @return {BasicForm} this
10058 reset : function(){
10059 var items = this.getItems();
10060 items.each(function(f){
10064 Roo.each(this.childForms || [], function (f) {
10072 getItems : function()
10074 var r=new Roo.util.MixedCollection(false, function(o){
10075 return o.id || (o.id = Roo.id());
10077 var iter = function(el) {
10084 Roo.each(el.items,function(e) {
10093 hideFields : function(items)
10095 Roo.each(items, function(i){
10097 var f = this.findField(i);
10108 showFields : function(items)
10110 Roo.each(items, function(i){
10112 var f = this.findField(i);
10125 Roo.apply(Roo.bootstrap.Form, {
10141 intervalID : false,
10147 if(this.isApplied){
10152 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10153 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10154 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10155 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10158 this.maskEl.top.enableDisplayMode("block");
10159 this.maskEl.left.enableDisplayMode("block");
10160 this.maskEl.bottom.enableDisplayMode("block");
10161 this.maskEl.right.enableDisplayMode("block");
10163 this.toolTip = new Roo.bootstrap.Tooltip({
10164 cls : 'roo-form-error-popover',
10166 'left' : ['r-l', [-2,0], 'right'],
10167 'right' : ['l-r', [2,0], 'left'],
10168 'bottom' : ['tl-bl', [0,2], 'top'],
10169 'top' : [ 'bl-tl', [0,-2], 'bottom']
10173 this.toolTip.render(Roo.get(document.body));
10175 this.toolTip.el.enableDisplayMode("block");
10177 Roo.get(document.body).on('click', function(){
10181 Roo.get(document.body).on('touchstart', function(){
10185 this.isApplied = true
10188 mask : function(form, target)
10192 this.target = target;
10194 if(!this.form.errorMask || !target.el){
10198 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10200 Roo.log(scrollable);
10202 var ot = this.target.el.calcOffsetsTo(scrollable);
10204 var scrollTo = ot[1] - this.form.maskOffset;
10206 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10208 scrollable.scrollTo('top', scrollTo);
10210 var box = this.target.el.getBox();
10212 var zIndex = Roo.bootstrap.Modal.zIndex++;
10215 this.maskEl.top.setStyle('position', 'absolute');
10216 this.maskEl.top.setStyle('z-index', zIndex);
10217 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10218 this.maskEl.top.setLeft(0);
10219 this.maskEl.top.setTop(0);
10220 this.maskEl.top.show();
10222 this.maskEl.left.setStyle('position', 'absolute');
10223 this.maskEl.left.setStyle('z-index', zIndex);
10224 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10225 this.maskEl.left.setLeft(0);
10226 this.maskEl.left.setTop(box.y - this.padding);
10227 this.maskEl.left.show();
10229 this.maskEl.bottom.setStyle('position', 'absolute');
10230 this.maskEl.bottom.setStyle('z-index', zIndex);
10231 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10232 this.maskEl.bottom.setLeft(0);
10233 this.maskEl.bottom.setTop(box.bottom + this.padding);
10234 this.maskEl.bottom.show();
10236 this.maskEl.right.setStyle('position', 'absolute');
10237 this.maskEl.right.setStyle('z-index', zIndex);
10238 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10239 this.maskEl.right.setLeft(box.right + this.padding);
10240 this.maskEl.right.setTop(box.y - this.padding);
10241 this.maskEl.right.show();
10243 this.toolTip.bindEl = this.target.el;
10245 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10247 var tip = this.target.blankText;
10249 if(this.target.getValue() !== '' ) {
10251 if (this.target.invalidText.length) {
10252 tip = this.target.invalidText;
10253 } else if (this.target.regexText.length){
10254 tip = this.target.regexText;
10258 this.toolTip.show(tip);
10260 this.intervalID = window.setInterval(function() {
10261 Roo.bootstrap.Form.popover.unmask();
10264 window.onwheel = function(){ return false;};
10266 (function(){ this.isMasked = true; }).defer(500, this);
10270 unmask : function()
10272 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10276 this.maskEl.top.setStyle('position', 'absolute');
10277 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10278 this.maskEl.top.hide();
10280 this.maskEl.left.setStyle('position', 'absolute');
10281 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10282 this.maskEl.left.hide();
10284 this.maskEl.bottom.setStyle('position', 'absolute');
10285 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10286 this.maskEl.bottom.hide();
10288 this.maskEl.right.setStyle('position', 'absolute');
10289 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10290 this.maskEl.right.hide();
10292 this.toolTip.hide();
10294 this.toolTip.el.hide();
10296 window.onwheel = function(){ return true;};
10298 if(this.intervalID){
10299 window.clearInterval(this.intervalID);
10300 this.intervalID = false;
10303 this.isMasked = false;
10313 * Ext JS Library 1.1.1
10314 * Copyright(c) 2006-2007, Ext JS, LLC.
10316 * Originally Released Under LGPL - original licence link has changed is not relivant.
10319 * <script type="text/javascript">
10322 * @class Roo.form.VTypes
10323 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10326 Roo.form.VTypes = function(){
10327 // closure these in so they are only created once.
10328 var alpha = /^[a-zA-Z_]+$/;
10329 var alphanum = /^[a-zA-Z0-9_]+$/;
10330 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10331 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10333 // All these messages and functions are configurable
10336 * The function used to validate email addresses
10337 * @param {String} value The email address
10339 'email' : function(v){
10340 return email.test(v);
10343 * The error text to display when the email validation function returns false
10346 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10348 * The keystroke filter mask to be applied on email input
10351 'emailMask' : /[a-z0-9_\.\-@]/i,
10354 * The function used to validate URLs
10355 * @param {String} value The URL
10357 'url' : function(v){
10358 return url.test(v);
10361 * The error text to display when the url validation function returns false
10364 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10367 * The function used to validate alpha values
10368 * @param {String} value The value
10370 'alpha' : function(v){
10371 return alpha.test(v);
10374 * The error text to display when the alpha validation function returns false
10377 'alphaText' : 'This field should only contain letters and _',
10379 * The keystroke filter mask to be applied on alpha input
10382 'alphaMask' : /[a-z_]/i,
10385 * The function used to validate alphanumeric values
10386 * @param {String} value The value
10388 'alphanum' : function(v){
10389 return alphanum.test(v);
10392 * The error text to display when the alphanumeric validation function returns false
10395 'alphanumText' : 'This field should only contain letters, numbers and _',
10397 * The keystroke filter mask to be applied on alphanumeric input
10400 'alphanumMask' : /[a-z0-9_]/i
10410 * @class Roo.bootstrap.Input
10411 * @extends Roo.bootstrap.Component
10412 * Bootstrap Input class
10413 * @cfg {Boolean} disabled is it disabled
10414 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10415 * @cfg {String} name name of the input
10416 * @cfg {string} fieldLabel - the label associated
10417 * @cfg {string} placeholder - placeholder to put in text.
10418 * @cfg {string} before - input group add on before
10419 * @cfg {string} after - input group add on after
10420 * @cfg {string} size - (lg|sm) or leave empty..
10421 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10422 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10423 * @cfg {Number} md colspan out of 12 for computer-sized screens
10424 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10425 * @cfg {string} value default value of the input
10426 * @cfg {Number} labelWidth set the width of label
10427 * @cfg {Number} labellg set the width of label (1-12)
10428 * @cfg {Number} labelmd set the width of label (1-12)
10429 * @cfg {Number} labelsm set the width of label (1-12)
10430 * @cfg {Number} labelxs set the width of label (1-12)
10431 * @cfg {String} labelAlign (top|left)
10432 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10433 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10434 * @cfg {String} indicatorpos (left|right) default left
10435 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10436 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10437 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10439 * @cfg {String} align (left|center|right) Default left
10440 * @cfg {Boolean} forceFeedback (true|false) Default false
10443 * Create a new Input
10444 * @param {Object} config The config object
10447 Roo.bootstrap.Input = function(config){
10449 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10454 * Fires when this field receives input focus.
10455 * @param {Roo.form.Field} this
10460 * Fires when this field loses input focus.
10461 * @param {Roo.form.Field} this
10465 * @event specialkey
10466 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10467 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10468 * @param {Roo.form.Field} this
10469 * @param {Roo.EventObject} e The event object
10474 * Fires just before the field blurs if the field value has changed.
10475 * @param {Roo.form.Field} this
10476 * @param {Mixed} newValue The new value
10477 * @param {Mixed} oldValue The original value
10482 * Fires after the field has been marked as invalid.
10483 * @param {Roo.form.Field} this
10484 * @param {String} msg The validation message
10489 * Fires after the field has been validated with no errors.
10490 * @param {Roo.form.Field} this
10495 * Fires after the key up
10496 * @param {Roo.form.Field} this
10497 * @param {Roo.EventObject} e The event Object
10503 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10505 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10506 automatic validation (defaults to "keyup").
10508 validationEvent : "keyup",
10510 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10512 validateOnBlur : true,
10514 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10516 validationDelay : 250,
10518 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10520 focusClass : "x-form-focus", // not needed???
10524 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10526 invalidClass : "has-warning",
10529 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10531 validClass : "has-success",
10534 * @cfg {Boolean} hasFeedback (true|false) default true
10536 hasFeedback : true,
10539 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10541 invalidFeedbackClass : "glyphicon-warning-sign",
10544 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10546 validFeedbackClass : "glyphicon-ok",
10549 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10551 selectOnFocus : false,
10554 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10558 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10563 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10565 disableKeyFilter : false,
10568 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10572 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10576 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10578 blankText : "Please complete this mandatory field",
10581 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10585 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10587 maxLength : Number.MAX_VALUE,
10589 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10591 minLengthText : "The minimum length for this field is {0}",
10593 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10595 maxLengthText : "The maximum length for this field is {0}",
10599 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10600 * If available, this function will be called only after the basic validators all return true, and will be passed the
10601 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10605 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10606 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10607 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10611 * @cfg {String} regexText -- Depricated - use Invalid Text
10616 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10622 autocomplete: false,
10626 inputType : 'text',
10629 placeholder: false,
10634 preventMark: false,
10635 isFormField : true,
10638 labelAlign : false,
10641 formatedValue : false,
10642 forceFeedback : false,
10644 indicatorpos : 'left',
10654 parentLabelAlign : function()
10657 while (parent.parent()) {
10658 parent = parent.parent();
10659 if (typeof(parent.labelAlign) !='undefined') {
10660 return parent.labelAlign;
10667 getAutoCreate : function()
10669 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10675 if(this.inputType != 'hidden'){
10676 cfg.cls = 'form-group' //input-group
10682 type : this.inputType,
10683 value : this.value,
10684 cls : 'form-control',
10685 placeholder : this.placeholder || '',
10686 autocomplete : this.autocomplete || 'new-password'
10688 if (this.inputType == 'file') {
10689 input.style = 'overflow:hidden'; // why not in CSS?
10692 if(this.capture.length){
10693 input.capture = this.capture;
10696 if(this.accept.length){
10697 input.accept = this.accept + "/*";
10701 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10704 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10705 input.maxLength = this.maxLength;
10708 if (this.disabled) {
10709 input.disabled=true;
10712 if (this.readOnly) {
10713 input.readonly=true;
10717 input.name = this.name;
10721 input.cls += ' input-' + this.size;
10725 ['xs','sm','md','lg'].map(function(size){
10726 if (settings[size]) {
10727 cfg.cls += ' col-' + size + '-' + settings[size];
10731 var inputblock = input;
10735 cls: 'glyphicon form-control-feedback'
10738 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10741 cls : 'has-feedback',
10749 if (this.before || this.after) {
10752 cls : 'input-group',
10756 if (this.before && typeof(this.before) == 'string') {
10758 inputblock.cn.push({
10760 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10764 if (this.before && typeof(this.before) == 'object') {
10765 this.before = Roo.factory(this.before);
10767 inputblock.cn.push({
10769 cls : 'roo-input-before input-group-prepend input-group-' +
10770 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10774 inputblock.cn.push(input);
10776 if (this.after && typeof(this.after) == 'string') {
10777 inputblock.cn.push({
10779 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10783 if (this.after && typeof(this.after) == 'object') {
10784 this.after = Roo.factory(this.after);
10786 inputblock.cn.push({
10788 cls : 'roo-input-after input-group-append input-group-' +
10789 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10793 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10794 inputblock.cls += ' has-feedback';
10795 inputblock.cn.push(feedback);
10800 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10801 tooltip : 'This field is required'
10803 if (this.allowBlank ) {
10804 indicator.style = this.allowBlank ? ' display:none' : '';
10806 if (align ==='left' && this.fieldLabel.length) {
10808 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10815 cls : 'control-label col-form-label',
10816 html : this.fieldLabel
10827 var labelCfg = cfg.cn[1];
10828 var contentCfg = cfg.cn[2];
10830 if(this.indicatorpos == 'right'){
10835 cls : 'control-label col-form-label',
10839 html : this.fieldLabel
10853 labelCfg = cfg.cn[0];
10854 contentCfg = cfg.cn[1];
10858 if(this.labelWidth > 12){
10859 labelCfg.style = "width: " + this.labelWidth + 'px';
10862 if(this.labelWidth < 13 && this.labelmd == 0){
10863 this.labelmd = this.labelWidth;
10866 if(this.labellg > 0){
10867 labelCfg.cls += ' col-lg-' + this.labellg;
10868 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10871 if(this.labelmd > 0){
10872 labelCfg.cls += ' col-md-' + this.labelmd;
10873 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10876 if(this.labelsm > 0){
10877 labelCfg.cls += ' col-sm-' + this.labelsm;
10878 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10881 if(this.labelxs > 0){
10882 labelCfg.cls += ' col-xs-' + this.labelxs;
10883 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10887 } else if ( this.fieldLabel.length) {
10894 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10895 tooltip : 'This field is required',
10896 style : this.allowBlank ? ' display:none' : ''
10900 //cls : 'input-group-addon',
10901 html : this.fieldLabel
10909 if(this.indicatorpos == 'right'){
10914 //cls : 'input-group-addon',
10915 html : this.fieldLabel
10920 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10921 tooltip : 'This field is required',
10922 style : this.allowBlank ? ' display:none' : ''
10942 if (this.parentType === 'Navbar' && this.parent().bar) {
10943 cfg.cls += ' navbar-form';
10946 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10947 // on BS4 we do this only if not form
10948 cfg.cls += ' navbar-form';
10956 * return the real input element.
10958 inputEl: function ()
10960 return this.el.select('input.form-control',true).first();
10963 tooltipEl : function()
10965 return this.inputEl();
10968 indicatorEl : function()
10970 if (Roo.bootstrap.version == 4) {
10971 return false; // not enabled in v4 yet.
10974 var indicator = this.el.select('i.roo-required-indicator',true).first();
10984 setDisabled : function(v)
10986 var i = this.inputEl().dom;
10988 i.removeAttribute('disabled');
10992 i.setAttribute('disabled','true');
10994 initEvents : function()
10997 this.inputEl().on("keydown" , this.fireKey, this);
10998 this.inputEl().on("focus", this.onFocus, this);
10999 this.inputEl().on("blur", this.onBlur, this);
11001 this.inputEl().relayEvent('keyup', this);
11003 this.indicator = this.indicatorEl();
11005 if(this.indicator){
11006 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11009 // reference to original value for reset
11010 this.originalValue = this.getValue();
11011 //Roo.form.TextField.superclass.initEvents.call(this);
11012 if(this.validationEvent == 'keyup'){
11013 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11014 this.inputEl().on('keyup', this.filterValidation, this);
11016 else if(this.validationEvent !== false){
11017 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11020 if(this.selectOnFocus){
11021 this.on("focus", this.preFocus, this);
11024 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11025 this.inputEl().on("keypress", this.filterKeys, this);
11027 this.inputEl().relayEvent('keypress', this);
11030 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11031 this.el.on("click", this.autoSize, this);
11034 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11035 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11038 if (typeof(this.before) == 'object') {
11039 this.before.render(this.el.select('.roo-input-before',true).first());
11041 if (typeof(this.after) == 'object') {
11042 this.after.render(this.el.select('.roo-input-after',true).first());
11045 this.inputEl().on('change', this.onChange, this);
11048 filterValidation : function(e){
11049 if(!e.isNavKeyPress()){
11050 this.validationTask.delay(this.validationDelay);
11054 * Validates the field value
11055 * @return {Boolean} True if the value is valid, else false
11057 validate : function(){
11058 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11059 if(this.disabled || this.validateValue(this.getRawValue())){
11064 this.markInvalid();
11070 * Validates a value according to the field's validation rules and marks the field as invalid
11071 * if the validation fails
11072 * @param {Mixed} value The value to validate
11073 * @return {Boolean} True if the value is valid, else false
11075 validateValue : function(value)
11077 if(this.getVisibilityEl().hasClass('hidden')){
11081 if(value.length < 1) { // if it's blank
11082 if(this.allowBlank){
11088 if(value.length < this.minLength){
11091 if(value.length > this.maxLength){
11095 var vt = Roo.form.VTypes;
11096 if(!vt[this.vtype](value, this)){
11100 if(typeof this.validator == "function"){
11101 var msg = this.validator(value);
11105 if (typeof(msg) == 'string') {
11106 this.invalidText = msg;
11110 if(this.regex && !this.regex.test(value)){
11118 fireKey : function(e){
11119 //Roo.log('field ' + e.getKey());
11120 if(e.isNavKeyPress()){
11121 this.fireEvent("specialkey", this, e);
11124 focus : function (selectText){
11126 this.inputEl().focus();
11127 if(selectText === true){
11128 this.inputEl().dom.select();
11134 onFocus : function(){
11135 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11136 // this.el.addClass(this.focusClass);
11138 if(!this.hasFocus){
11139 this.hasFocus = true;
11140 this.startValue = this.getValue();
11141 this.fireEvent("focus", this);
11145 beforeBlur : Roo.emptyFn,
11149 onBlur : function(){
11151 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11152 //this.el.removeClass(this.focusClass);
11154 this.hasFocus = false;
11155 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11158 var v = this.getValue();
11159 if(String(v) !== String(this.startValue)){
11160 this.fireEvent('change', this, v, this.startValue);
11162 this.fireEvent("blur", this);
11165 onChange : function(e)
11167 var v = this.getValue();
11168 if(String(v) !== String(this.startValue)){
11169 this.fireEvent('change', this, v, this.startValue);
11175 * Resets the current field value to the originally loaded value and clears any validation messages
11177 reset : function(){
11178 this.setValue(this.originalValue);
11182 * Returns the name of the field
11183 * @return {Mixed} name The name field
11185 getName: function(){
11189 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11190 * @return {Mixed} value The field value
11192 getValue : function(){
11194 var v = this.inputEl().getValue();
11199 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11200 * @return {Mixed} value The field value
11202 getRawValue : function(){
11203 var v = this.inputEl().getValue();
11209 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11210 * @param {Mixed} value The value to set
11212 setRawValue : function(v){
11213 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11216 selectText : function(start, end){
11217 var v = this.getRawValue();
11219 start = start === undefined ? 0 : start;
11220 end = end === undefined ? v.length : end;
11221 var d = this.inputEl().dom;
11222 if(d.setSelectionRange){
11223 d.setSelectionRange(start, end);
11224 }else if(d.createTextRange){
11225 var range = d.createTextRange();
11226 range.moveStart("character", start);
11227 range.moveEnd("character", v.length-end);
11234 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11235 * @param {Mixed} value The value to set
11237 setValue : function(v){
11240 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11246 processValue : function(value){
11247 if(this.stripCharsRe){
11248 var newValue = value.replace(this.stripCharsRe, '');
11249 if(newValue !== value){
11250 this.setRawValue(newValue);
11257 preFocus : function(){
11259 if(this.selectOnFocus){
11260 this.inputEl().dom.select();
11263 filterKeys : function(e){
11264 var k = e.getKey();
11265 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11268 var c = e.getCharCode(), cc = String.fromCharCode(c);
11269 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11272 if(!this.maskRe.test(cc)){
11277 * Clear any invalid styles/messages for this field
11279 clearInvalid : function(){
11281 if(!this.el || this.preventMark){ // not rendered
11286 this.el.removeClass([this.invalidClass, 'is-invalid']);
11288 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11290 var feedback = this.el.select('.form-control-feedback', true).first();
11293 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11298 if(this.indicator){
11299 this.indicator.removeClass('visible');
11300 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11303 this.fireEvent('valid', this);
11307 * Mark this field as valid
11309 markValid : function()
11311 if(!this.el || this.preventMark){ // not rendered...
11315 this.el.removeClass([this.invalidClass, this.validClass]);
11316 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11318 var feedback = this.el.select('.form-control-feedback', true).first();
11321 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11324 if(this.indicator){
11325 this.indicator.removeClass('visible');
11326 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11334 if(this.allowBlank && !this.getRawValue().length){
11337 if (Roo.bootstrap.version == 3) {
11338 this.el.addClass(this.validClass);
11340 this.inputEl().addClass('is-valid');
11343 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11345 var feedback = this.el.select('.form-control-feedback', true).first();
11348 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11349 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11354 this.fireEvent('valid', this);
11358 * Mark this field as invalid
11359 * @param {String} msg The validation message
11361 markInvalid : function(msg)
11363 if(!this.el || this.preventMark){ // not rendered
11367 this.el.removeClass([this.invalidClass, this.validClass]);
11368 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11370 var feedback = this.el.select('.form-control-feedback', true).first();
11373 this.el.select('.form-control-feedback', true).first().removeClass(
11374 [this.invalidFeedbackClass, this.validFeedbackClass]);
11381 if(this.allowBlank && !this.getRawValue().length){
11385 if(this.indicator){
11386 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11387 this.indicator.addClass('visible');
11389 if (Roo.bootstrap.version == 3) {
11390 this.el.addClass(this.invalidClass);
11392 this.inputEl().addClass('is-invalid');
11397 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11399 var feedback = this.el.select('.form-control-feedback', true).first();
11402 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11404 if(this.getValue().length || this.forceFeedback){
11405 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11412 this.fireEvent('invalid', this, msg);
11415 SafariOnKeyDown : function(event)
11417 // this is a workaround for a password hang bug on chrome/ webkit.
11418 if (this.inputEl().dom.type != 'password') {
11422 var isSelectAll = false;
11424 if(this.inputEl().dom.selectionEnd > 0){
11425 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11427 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11428 event.preventDefault();
11433 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11435 event.preventDefault();
11436 // this is very hacky as keydown always get's upper case.
11438 var cc = String.fromCharCode(event.getCharCode());
11439 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11443 adjustWidth : function(tag, w){
11444 tag = tag.toLowerCase();
11445 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11446 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11447 if(tag == 'input'){
11450 if(tag == 'textarea'){
11453 }else if(Roo.isOpera){
11454 if(tag == 'input'){
11457 if(tag == 'textarea'){
11465 setFieldLabel : function(v)
11467 if(!this.rendered){
11471 if(this.indicatorEl()){
11472 var ar = this.el.select('label > span',true);
11474 if (ar.elements.length) {
11475 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11476 this.fieldLabel = v;
11480 var br = this.el.select('label',true);
11482 if(br.elements.length) {
11483 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11484 this.fieldLabel = v;
11488 Roo.log('Cannot Found any of label > span || label in input');
11492 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11493 this.fieldLabel = v;
11508 * @class Roo.bootstrap.TextArea
11509 * @extends Roo.bootstrap.Input
11510 * Bootstrap TextArea class
11511 * @cfg {Number} cols Specifies the visible width of a text area
11512 * @cfg {Number} rows Specifies the visible number of lines in a text area
11513 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11514 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11515 * @cfg {string} html text
11518 * Create a new TextArea
11519 * @param {Object} config The config object
11522 Roo.bootstrap.TextArea = function(config){
11523 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11527 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11537 getAutoCreate : function(){
11539 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11545 if(this.inputType != 'hidden'){
11546 cfg.cls = 'form-group' //input-group
11554 value : this.value || '',
11555 html: this.html || '',
11556 cls : 'form-control',
11557 placeholder : this.placeholder || ''
11561 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11562 input.maxLength = this.maxLength;
11566 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11570 input.cols = this.cols;
11573 if (this.readOnly) {
11574 input.readonly = true;
11578 input.name = this.name;
11582 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11586 ['xs','sm','md','lg'].map(function(size){
11587 if (settings[size]) {
11588 cfg.cls += ' col-' + size + '-' + settings[size];
11592 var inputblock = input;
11594 if(this.hasFeedback && !this.allowBlank){
11598 cls: 'glyphicon form-control-feedback'
11602 cls : 'has-feedback',
11611 if (this.before || this.after) {
11614 cls : 'input-group',
11618 inputblock.cn.push({
11620 cls : 'input-group-addon',
11625 inputblock.cn.push(input);
11627 if(this.hasFeedback && !this.allowBlank){
11628 inputblock.cls += ' has-feedback';
11629 inputblock.cn.push(feedback);
11633 inputblock.cn.push({
11635 cls : 'input-group-addon',
11642 if (align ==='left' && this.fieldLabel.length) {
11647 cls : 'control-label',
11648 html : this.fieldLabel
11659 if(this.labelWidth > 12){
11660 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11663 if(this.labelWidth < 13 && this.labelmd == 0){
11664 this.labelmd = this.labelWidth;
11667 if(this.labellg > 0){
11668 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11669 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11672 if(this.labelmd > 0){
11673 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11674 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11677 if(this.labelsm > 0){
11678 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11679 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11682 if(this.labelxs > 0){
11683 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11684 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11687 } else if ( this.fieldLabel.length) {
11692 //cls : 'input-group-addon',
11693 html : this.fieldLabel
11711 if (this.disabled) {
11712 input.disabled=true;
11719 * return the real textarea element.
11721 inputEl: function ()
11723 return this.el.select('textarea.form-control',true).first();
11727 * Clear any invalid styles/messages for this field
11729 clearInvalid : function()
11732 if(!this.el || this.preventMark){ // not rendered
11736 var label = this.el.select('label', true).first();
11737 var icon = this.el.select('i.fa-star', true).first();
11742 this.el.removeClass( this.validClass);
11743 this.inputEl().removeClass('is-invalid');
11745 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11747 var feedback = this.el.select('.form-control-feedback', true).first();
11750 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11755 this.fireEvent('valid', this);
11759 * Mark this field as valid
11761 markValid : function()
11763 if(!this.el || this.preventMark){ // not rendered
11767 this.el.removeClass([this.invalidClass, this.validClass]);
11768 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11770 var feedback = this.el.select('.form-control-feedback', true).first();
11773 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11776 if(this.disabled || this.allowBlank){
11780 var label = this.el.select('label', true).first();
11781 var icon = this.el.select('i.fa-star', true).first();
11786 if (Roo.bootstrap.version == 3) {
11787 this.el.addClass(this.validClass);
11789 this.inputEl().addClass('is-valid');
11793 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11795 var feedback = this.el.select('.form-control-feedback', true).first();
11798 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11799 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11804 this.fireEvent('valid', this);
11808 * Mark this field as invalid
11809 * @param {String} msg The validation message
11811 markInvalid : function(msg)
11813 if(!this.el || this.preventMark){ // not rendered
11817 this.el.removeClass([this.invalidClass, this.validClass]);
11818 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11820 var feedback = this.el.select('.form-control-feedback', true).first();
11823 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11826 if(this.disabled || this.allowBlank){
11830 var label = this.el.select('label', true).first();
11831 var icon = this.el.select('i.fa-star', true).first();
11833 if(!this.getValue().length && label && !icon){
11834 this.el.createChild({
11836 cls : 'text-danger fa fa-lg fa-star',
11837 tooltip : 'This field is required',
11838 style : 'margin-right:5px;'
11842 if (Roo.bootstrap.version == 3) {
11843 this.el.addClass(this.invalidClass);
11845 this.inputEl().addClass('is-invalid');
11848 // fixme ... this may be depricated need to test..
11849 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11851 var feedback = this.el.select('.form-control-feedback', true).first();
11854 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11856 if(this.getValue().length || this.forceFeedback){
11857 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11864 this.fireEvent('invalid', this, msg);
11872 * trigger field - base class for combo..
11877 * @class Roo.bootstrap.TriggerField
11878 * @extends Roo.bootstrap.Input
11879 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11880 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11881 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11882 * for which you can provide a custom implementation. For example:
11884 var trigger = new Roo.bootstrap.TriggerField();
11885 trigger.onTriggerClick = myTriggerFn;
11886 trigger.applyTo('my-field');
11889 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11890 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11891 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11892 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11893 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11896 * Create a new TriggerField.
11897 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11898 * to the base TextField)
11900 Roo.bootstrap.TriggerField = function(config){
11901 this.mimicing = false;
11902 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11905 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11907 * @cfg {String} triggerClass A CSS class to apply to the trigger
11910 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11915 * @cfg {Boolean} removable (true|false) special filter default false
11919 /** @cfg {Boolean} grow @hide */
11920 /** @cfg {Number} growMin @hide */
11921 /** @cfg {Number} growMax @hide */
11927 autoSize: Roo.emptyFn,
11931 deferHeight : true,
11934 actionMode : 'wrap',
11939 getAutoCreate : function(){
11941 var align = this.labelAlign || this.parentLabelAlign();
11946 cls: 'form-group' //input-group
11953 type : this.inputType,
11954 cls : 'form-control',
11955 autocomplete: 'new-password',
11956 placeholder : this.placeholder || ''
11960 input.name = this.name;
11963 input.cls += ' input-' + this.size;
11966 if (this.disabled) {
11967 input.disabled=true;
11970 var inputblock = input;
11972 if(this.hasFeedback && !this.allowBlank){
11976 cls: 'glyphicon form-control-feedback'
11979 if(this.removable && !this.editable ){
11981 cls : 'has-feedback',
11987 cls : 'roo-combo-removable-btn close'
11994 cls : 'has-feedback',
12003 if(this.removable && !this.editable ){
12005 cls : 'roo-removable',
12011 cls : 'roo-combo-removable-btn close'
12018 if (this.before || this.after) {
12021 cls : 'input-group',
12025 inputblock.cn.push({
12027 cls : 'input-group-addon input-group-prepend input-group-text',
12032 inputblock.cn.push(input);
12034 if(this.hasFeedback && !this.allowBlank){
12035 inputblock.cls += ' has-feedback';
12036 inputblock.cn.push(feedback);
12040 inputblock.cn.push({
12042 cls : 'input-group-addon input-group-append input-group-text',
12051 var ibwrap = inputblock;
12056 cls: 'roo-select2-choices',
12060 cls: 'roo-select2-search-field',
12072 cls: 'roo-select2-container input-group',
12077 cls: 'form-hidden-field'
12083 if(!this.multiple && this.showToggleBtn){
12089 if (this.caret != false) {
12092 cls: 'fa fa-' + this.caret
12099 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12101 Roo.bootstrap.version == 3 ? caret : '',
12104 cls: 'combobox-clear',
12118 combobox.cls += ' roo-select2-container-multi';
12122 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12123 tooltip : 'This field is required'
12125 if (Roo.bootstrap.version == 4) {
12128 style : 'display:none'
12133 if (align ==='left' && this.fieldLabel.length) {
12135 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12142 cls : 'control-label',
12143 html : this.fieldLabel
12155 var labelCfg = cfg.cn[1];
12156 var contentCfg = cfg.cn[2];
12158 if(this.indicatorpos == 'right'){
12163 cls : 'control-label',
12167 html : this.fieldLabel
12181 labelCfg = cfg.cn[0];
12182 contentCfg = cfg.cn[1];
12185 if(this.labelWidth > 12){
12186 labelCfg.style = "width: " + this.labelWidth + 'px';
12189 if(this.labelWidth < 13 && this.labelmd == 0){
12190 this.labelmd = this.labelWidth;
12193 if(this.labellg > 0){
12194 labelCfg.cls += ' col-lg-' + this.labellg;
12195 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12198 if(this.labelmd > 0){
12199 labelCfg.cls += ' col-md-' + this.labelmd;
12200 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12203 if(this.labelsm > 0){
12204 labelCfg.cls += ' col-sm-' + this.labelsm;
12205 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12208 if(this.labelxs > 0){
12209 labelCfg.cls += ' col-xs-' + this.labelxs;
12210 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12213 } else if ( this.fieldLabel.length) {
12214 // Roo.log(" label");
12219 //cls : 'input-group-addon',
12220 html : this.fieldLabel
12228 if(this.indicatorpos == 'right'){
12236 html : this.fieldLabel
12250 // Roo.log(" no label && no align");
12257 ['xs','sm','md','lg'].map(function(size){
12258 if (settings[size]) {
12259 cfg.cls += ' col-' + size + '-' + settings[size];
12270 onResize : function(w, h){
12271 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12272 // if(typeof w == 'number'){
12273 // var x = w - this.trigger.getWidth();
12274 // this.inputEl().setWidth(this.adjustWidth('input', x));
12275 // this.trigger.setStyle('left', x+'px');
12280 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12283 getResizeEl : function(){
12284 return this.inputEl();
12288 getPositionEl : function(){
12289 return this.inputEl();
12293 alignErrorIcon : function(){
12294 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12298 initEvents : function(){
12302 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12303 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12304 if(!this.multiple && this.showToggleBtn){
12305 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12306 if(this.hideTrigger){
12307 this.trigger.setDisplayed(false);
12309 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12313 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12316 if(this.removable && !this.editable && !this.tickable){
12317 var close = this.closeTriggerEl();
12320 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12321 close.on('click', this.removeBtnClick, this, close);
12325 //this.trigger.addClassOnOver('x-form-trigger-over');
12326 //this.trigger.addClassOnClick('x-form-trigger-click');
12329 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12333 closeTriggerEl : function()
12335 var close = this.el.select('.roo-combo-removable-btn', true).first();
12336 return close ? close : false;
12339 removeBtnClick : function(e, h, el)
12341 e.preventDefault();
12343 if(this.fireEvent("remove", this) !== false){
12345 this.fireEvent("afterremove", this)
12349 createList : function()
12351 this.list = Roo.get(document.body).createChild({
12352 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12353 cls: 'typeahead typeahead-long dropdown-menu shadow',
12354 style: 'display:none'
12357 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12362 initTrigger : function(){
12367 onDestroy : function(){
12369 this.trigger.removeAllListeners();
12370 // this.trigger.remove();
12373 // this.wrap.remove();
12375 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12379 onFocus : function(){
12380 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12382 if(!this.mimicing){
12383 this.wrap.addClass('x-trigger-wrap-focus');
12384 this.mimicing = true;
12385 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12386 if(this.monitorTab){
12387 this.el.on("keydown", this.checkTab, this);
12394 checkTab : function(e){
12395 if(e.getKey() == e.TAB){
12396 this.triggerBlur();
12401 onBlur : function(){
12406 mimicBlur : function(e, t){
12408 if(!this.wrap.contains(t) && this.validateBlur()){
12409 this.triggerBlur();
12415 triggerBlur : function(){
12416 this.mimicing = false;
12417 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12418 if(this.monitorTab){
12419 this.el.un("keydown", this.checkTab, this);
12421 //this.wrap.removeClass('x-trigger-wrap-focus');
12422 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12426 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12427 validateBlur : function(e, t){
12432 onDisable : function(){
12433 this.inputEl().dom.disabled = true;
12434 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12436 // this.wrap.addClass('x-item-disabled');
12441 onEnable : function(){
12442 this.inputEl().dom.disabled = false;
12443 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12445 // this.el.removeClass('x-item-disabled');
12450 onShow : function(){
12451 var ae = this.getActionEl();
12454 ae.dom.style.display = '';
12455 ae.dom.style.visibility = 'visible';
12461 onHide : function(){
12462 var ae = this.getActionEl();
12463 ae.dom.style.display = 'none';
12467 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12468 * by an implementing function.
12470 * @param {EventObject} e
12472 onTriggerClick : Roo.emptyFn
12480 * @class Roo.bootstrap.CardUploader
12481 * @extends Roo.bootstrap.Button
12482 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12483 * @cfg {Number} errorTimeout default 3000
12484 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12485 * @cfg {Array} html The button text.
12489 * Create a new CardUploader
12490 * @param {Object} config The config object
12493 Roo.bootstrap.CardUploader = function(config){
12497 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12500 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12508 * When a image is clicked on - and needs to display a slideshow or similar..
12509 * @param {Roo.bootstrap.Card} this
12510 * @param {Roo.bootstrap.Card} The card containing the image data (.data is a property with image info.)
12516 * When a the download link is clicked
12517 * @param {Roo.bootstrap.Card} this
12518 * @param {Roo.bootstrap.Card} The card containing the image data (.data is a property with image info.)
12526 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12529 errorTimeout : 3000,
12533 fileCollection : false,
12536 getAutoCreate : function()
12540 cls :'form-group' ,
12545 //cls : 'input-group-addon',
12546 html : this.fieldLabel
12554 value : this.value,
12555 cls : 'd-none form-control'
12560 multiple : 'multiple',
12562 cls : 'd-none roo-card-upload-selector'
12566 cls : 'roo-card-uploader-button-container w-100 mb-2'
12569 cls : 'card-columns roo-card-uploader-container'
12579 getChildContainer : function() /// what children are added to.
12581 return this.containerEl;
12584 getButtonContainer : function() /// what children are added to.
12586 return this.el.select(".roo-card-uploader-button-container").first();
12589 initEvents : function()
12592 Roo.bootstrap.Input.prototype.initEvents.call(this);
12596 xns: Roo.bootstrap,
12599 container_method : 'getButtonContainer' ,
12600 html : this.html, // fix changable?
12603 'click' : function(btn, e) {
12612 this.urlAPI = (window.createObjectURL && window) ||
12613 (window.URL && URL.revokeObjectURL && URL) ||
12614 (window.webkitURL && webkitURL);
12619 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12621 this.selectorEl.on('change', this.onFileSelected, this);
12624 this.images.forEach(function(img) {
12627 this.images = false;
12629 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12635 onClick : function(e)
12637 e.preventDefault();
12639 this.selectorEl.dom.click();
12643 onFileSelected : function(e)
12645 e.preventDefault();
12647 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12651 Roo.each(this.selectorEl.dom.files, function(file){
12652 this.addFile(file);
12661 addFile : function(file)
12664 if(typeof(file) === 'string'){
12665 throw "Add file by name?"; // should not happen
12669 if(!file || !this.urlAPI){
12679 var url = _this.urlAPI.createObjectURL( file);
12682 id : Roo.bootstrap.CardUploader.ID--,
12683 is_uploaded : false,
12687 mimetype : file.type,
12694 addCard : function (data)
12696 // hidden input element?
12697 // if the file is not an image...
12698 //then we need to use something other that and header_image
12703 xns : Roo.bootstrap,
12704 xtype : 'CardFooter',
12707 xns : Roo.bootstrap,
12713 xns : Roo.bootstrap,
12715 html : String.format("<small>{0}</small>", data.title),
12716 cls : 'col-10 text-left',
12721 click : function() {
12723 t.fireEvent( "download", t, data );
12729 xns : Roo.bootstrap,
12731 style: 'max-height: 28px; ',
12737 click : function() {
12738 t.removeCard(data.id)
12750 var cn = this.addxtype(
12753 xns : Roo.bootstrap,
12756 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12757 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12758 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12763 initEvents : function() {
12764 Roo.bootstrap.Card.prototype.initEvents.call(this);
12766 this.imgEl = this.el.select('.card-img-top').first();
12768 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
12769 this.imgEl.set({ 'pointer' : 'cursor' });
12772 this.getCardFooter().addClass('p-1');
12779 // dont' really need ot update items.
12780 // this.items.push(cn);
12781 this.fileCollection.add(cn);
12783 if (!data.srcfile) {
12784 this.updateInput();
12789 var reader = new FileReader();
12790 reader.addEventListener("load", function() {
12791 data.srcdata = reader.result;
12794 reader.readAsDataURL(data.srcfile);
12799 removeCard : function(id)
12802 var card = this.fileCollection.get(id);
12803 card.data.is_deleted = 1;
12804 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12805 //this.fileCollection.remove(card);
12806 //this.items = this.items.filter(function(e) { return e != card });
12807 // dont' really need ot update items.
12808 card.el.dom.parentNode.removeChild(card.el.dom);
12809 this.updateInput();
12815 this.fileCollection.each(function(card) {
12816 if (card.el.dom && card.el.dom.parentNode) {
12817 card.el.dom.parentNode.removeChild(card.el.dom);
12820 this.fileCollection.clear();
12821 this.updateInput();
12824 updateInput : function()
12827 this.fileCollection.each(function(e) {
12831 this.inputEl().dom.value = JSON.stringify(data);
12841 Roo.bootstrap.CardUploader.ID = -1;/*
12843 * Ext JS Library 1.1.1
12844 * Copyright(c) 2006-2007, Ext JS, LLC.
12846 * Originally Released Under LGPL - original licence link has changed is not relivant.
12849 * <script type="text/javascript">
12854 * @class Roo.data.SortTypes
12856 * Defines the default sorting (casting?) comparison functions used when sorting data.
12858 Roo.data.SortTypes = {
12860 * Default sort that does nothing
12861 * @param {Mixed} s The value being converted
12862 * @return {Mixed} The comparison value
12864 none : function(s){
12869 * The regular expression used to strip tags
12873 stripTagsRE : /<\/?[^>]+>/gi,
12876 * Strips all HTML tags to sort on text only
12877 * @param {Mixed} s The value being converted
12878 * @return {String} The comparison value
12880 asText : function(s){
12881 return String(s).replace(this.stripTagsRE, "");
12885 * Strips all HTML tags to sort on text only - Case insensitive
12886 * @param {Mixed} s The value being converted
12887 * @return {String} The comparison value
12889 asUCText : function(s){
12890 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12894 * Case insensitive string
12895 * @param {Mixed} s The value being converted
12896 * @return {String} The comparison value
12898 asUCString : function(s) {
12899 return String(s).toUpperCase();
12904 * @param {Mixed} s The value being converted
12905 * @return {Number} The comparison value
12907 asDate : function(s) {
12911 if(s instanceof Date){
12912 return s.getTime();
12914 return Date.parse(String(s));
12919 * @param {Mixed} s The value being converted
12920 * @return {Float} The comparison value
12922 asFloat : function(s) {
12923 var val = parseFloat(String(s).replace(/,/g, ""));
12932 * @param {Mixed} s The value being converted
12933 * @return {Number} The comparison value
12935 asInt : function(s) {
12936 var val = parseInt(String(s).replace(/,/g, ""));
12944 * Ext JS Library 1.1.1
12945 * Copyright(c) 2006-2007, Ext JS, LLC.
12947 * Originally Released Under LGPL - original licence link has changed is not relivant.
12950 * <script type="text/javascript">
12954 * @class Roo.data.Record
12955 * Instances of this class encapsulate both record <em>definition</em> information, and record
12956 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12957 * to access Records cached in an {@link Roo.data.Store} object.<br>
12959 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12960 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12963 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12965 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12966 * {@link #create}. The parameters are the same.
12967 * @param {Array} data An associative Array of data values keyed by the field name.
12968 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12969 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12970 * not specified an integer id is generated.
12972 Roo.data.Record = function(data, id){
12973 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12978 * Generate a constructor for a specific record layout.
12979 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12980 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12981 * Each field definition object may contain the following properties: <ul>
12982 * <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,
12983 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12984 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12985 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12986 * is being used, then this is a string containing the javascript expression to reference the data relative to
12987 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12988 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12989 * this may be omitted.</p></li>
12990 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12991 * <ul><li>auto (Default, implies no conversion)</li>
12996 * <li>date</li></ul></p></li>
12997 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12998 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12999 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13000 * by the Reader into an object that will be stored in the Record. It is passed the
13001 * following parameters:<ul>
13002 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13004 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13006 * <br>usage:<br><pre><code>
13007 var TopicRecord = Roo.data.Record.create(
13008 {name: 'title', mapping: 'topic_title'},
13009 {name: 'author', mapping: 'username'},
13010 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13011 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13012 {name: 'lastPoster', mapping: 'user2'},
13013 {name: 'excerpt', mapping: 'post_text'}
13016 var myNewRecord = new TopicRecord({
13017 title: 'Do my job please',
13020 lastPost: new Date(),
13021 lastPoster: 'Animal',
13022 excerpt: 'No way dude!'
13024 myStore.add(myNewRecord);
13029 Roo.data.Record.create = function(o){
13030 var f = function(){
13031 f.superclass.constructor.apply(this, arguments);
13033 Roo.extend(f, Roo.data.Record);
13034 var p = f.prototype;
13035 p.fields = new Roo.util.MixedCollection(false, function(field){
13038 for(var i = 0, len = o.length; i < len; i++){
13039 p.fields.add(new Roo.data.Field(o[i]));
13041 f.getField = function(name){
13042 return p.fields.get(name);
13047 Roo.data.Record.AUTO_ID = 1000;
13048 Roo.data.Record.EDIT = 'edit';
13049 Roo.data.Record.REJECT = 'reject';
13050 Roo.data.Record.COMMIT = 'commit';
13052 Roo.data.Record.prototype = {
13054 * Readonly flag - true if this record has been modified.
13063 join : function(store){
13064 this.store = store;
13068 * Set the named field to the specified value.
13069 * @param {String} name The name of the field to set.
13070 * @param {Object} value The value to set the field to.
13072 set : function(name, value){
13073 if(this.data[name] == value){
13077 if(!this.modified){
13078 this.modified = {};
13080 if(typeof this.modified[name] == 'undefined'){
13081 this.modified[name] = this.data[name];
13083 this.data[name] = value;
13084 if(!this.editing && this.store){
13085 this.store.afterEdit(this);
13090 * Get the value of the named field.
13091 * @param {String} name The name of the field to get the value of.
13092 * @return {Object} The value of the field.
13094 get : function(name){
13095 return this.data[name];
13099 beginEdit : function(){
13100 this.editing = true;
13101 this.modified = {};
13105 cancelEdit : function(){
13106 this.editing = false;
13107 delete this.modified;
13111 endEdit : function(){
13112 this.editing = false;
13113 if(this.dirty && this.store){
13114 this.store.afterEdit(this);
13119 * Usually called by the {@link Roo.data.Store} which owns the Record.
13120 * Rejects all changes made to the Record since either creation, or the last commit operation.
13121 * Modified fields are reverted to their original values.
13123 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13124 * of reject operations.
13126 reject : function(){
13127 var m = this.modified;
13129 if(typeof m[n] != "function"){
13130 this.data[n] = m[n];
13133 this.dirty = false;
13134 delete this.modified;
13135 this.editing = false;
13137 this.store.afterReject(this);
13142 * Usually called by the {@link Roo.data.Store} which owns the Record.
13143 * Commits all changes made to the Record since either creation, or the last commit operation.
13145 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13146 * of commit operations.
13148 commit : function(){
13149 this.dirty = false;
13150 delete this.modified;
13151 this.editing = false;
13153 this.store.afterCommit(this);
13158 hasError : function(){
13159 return this.error != null;
13163 clearError : function(){
13168 * Creates a copy of this record.
13169 * @param {String} id (optional) A new record id if you don't want to use this record's id
13172 copy : function(newId) {
13173 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13177 * Ext JS Library 1.1.1
13178 * Copyright(c) 2006-2007, Ext JS, LLC.
13180 * Originally Released Under LGPL - original licence link has changed is not relivant.
13183 * <script type="text/javascript">
13189 * @class Roo.data.Store
13190 * @extends Roo.util.Observable
13191 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13192 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13194 * 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
13195 * has no knowledge of the format of the data returned by the Proxy.<br>
13197 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13198 * instances from the data object. These records are cached and made available through accessor functions.
13200 * Creates a new Store.
13201 * @param {Object} config A config object containing the objects needed for the Store to access data,
13202 * and read the data into Records.
13204 Roo.data.Store = function(config){
13205 this.data = new Roo.util.MixedCollection(false);
13206 this.data.getKey = function(o){
13209 this.baseParams = {};
13211 this.paramNames = {
13216 "multisort" : "_multisort"
13219 if(config && config.data){
13220 this.inlineData = config.data;
13221 delete config.data;
13224 Roo.apply(this, config);
13226 if(this.reader){ // reader passed
13227 this.reader = Roo.factory(this.reader, Roo.data);
13228 this.reader.xmodule = this.xmodule || false;
13229 if(!this.recordType){
13230 this.recordType = this.reader.recordType;
13232 if(this.reader.onMetaChange){
13233 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13237 if(this.recordType){
13238 this.fields = this.recordType.prototype.fields;
13240 this.modified = [];
13244 * @event datachanged
13245 * Fires when the data cache has changed, and a widget which is using this Store
13246 * as a Record cache should refresh its view.
13247 * @param {Store} this
13249 datachanged : true,
13251 * @event metachange
13252 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13253 * @param {Store} this
13254 * @param {Object} meta The JSON metadata
13259 * Fires when Records have been added to the Store
13260 * @param {Store} this
13261 * @param {Roo.data.Record[]} records The array of Records added
13262 * @param {Number} index The index at which the record(s) were added
13267 * Fires when a Record has been removed from the Store
13268 * @param {Store} this
13269 * @param {Roo.data.Record} record The Record that was removed
13270 * @param {Number} index The index at which the record was removed
13275 * Fires when a Record has been updated
13276 * @param {Store} this
13277 * @param {Roo.data.Record} record The Record that was updated
13278 * @param {String} operation The update operation being performed. Value may be one of:
13280 Roo.data.Record.EDIT
13281 Roo.data.Record.REJECT
13282 Roo.data.Record.COMMIT
13288 * Fires when the data cache has been cleared.
13289 * @param {Store} this
13293 * @event beforeload
13294 * Fires before a request is made for a new data object. If the beforeload handler returns false
13295 * the load action will be canceled.
13296 * @param {Store} this
13297 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13301 * @event beforeloadadd
13302 * Fires after a new set of Records has been loaded.
13303 * @param {Store} this
13304 * @param {Roo.data.Record[]} records The Records that were loaded
13305 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13307 beforeloadadd : true,
13310 * Fires after a new set of Records has been loaded, before they are added to the store.
13311 * @param {Store} this
13312 * @param {Roo.data.Record[]} records The Records that were loaded
13313 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13314 * @params {Object} return from reader
13318 * @event loadexception
13319 * Fires if an exception occurs in the Proxy during loading.
13320 * Called with the signature of the Proxy's "loadexception" event.
13321 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13324 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13325 * @param {Object} load options
13326 * @param {Object} jsonData from your request (normally this contains the Exception)
13328 loadexception : true
13332 this.proxy = Roo.factory(this.proxy, Roo.data);
13333 this.proxy.xmodule = this.xmodule || false;
13334 this.relayEvents(this.proxy, ["loadexception"]);
13336 this.sortToggle = {};
13337 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13339 Roo.data.Store.superclass.constructor.call(this);
13341 if(this.inlineData){
13342 this.loadData(this.inlineData);
13343 delete this.inlineData;
13347 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13349 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13350 * without a remote query - used by combo/forms at present.
13354 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13357 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13360 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13361 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13364 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13365 * on any HTTP request
13368 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13371 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13375 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13376 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13378 remoteSort : false,
13381 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13382 * loaded or when a record is removed. (defaults to false).
13384 pruneModifiedRecords : false,
13387 lastOptions : null,
13390 * Add Records to the Store and fires the add event.
13391 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13393 add : function(records){
13394 records = [].concat(records);
13395 for(var i = 0, len = records.length; i < len; i++){
13396 records[i].join(this);
13398 var index = this.data.length;
13399 this.data.addAll(records);
13400 this.fireEvent("add", this, records, index);
13404 * Remove a Record from the Store and fires the remove event.
13405 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13407 remove : function(record){
13408 var index = this.data.indexOf(record);
13409 this.data.removeAt(index);
13411 if(this.pruneModifiedRecords){
13412 this.modified.remove(record);
13414 this.fireEvent("remove", this, record, index);
13418 * Remove all Records from the Store and fires the clear event.
13420 removeAll : function(){
13422 if(this.pruneModifiedRecords){
13423 this.modified = [];
13425 this.fireEvent("clear", this);
13429 * Inserts Records to the Store at the given index and fires the add event.
13430 * @param {Number} index The start index at which to insert the passed Records.
13431 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13433 insert : function(index, records){
13434 records = [].concat(records);
13435 for(var i = 0, len = records.length; i < len; i++){
13436 this.data.insert(index, records[i]);
13437 records[i].join(this);
13439 this.fireEvent("add", this, records, index);
13443 * Get the index within the cache of the passed Record.
13444 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13445 * @return {Number} The index of the passed Record. Returns -1 if not found.
13447 indexOf : function(record){
13448 return this.data.indexOf(record);
13452 * Get the index within the cache of the Record with the passed id.
13453 * @param {String} id The id of the Record to find.
13454 * @return {Number} The index of the Record. Returns -1 if not found.
13456 indexOfId : function(id){
13457 return this.data.indexOfKey(id);
13461 * Get the Record with the specified id.
13462 * @param {String} id The id of the Record to find.
13463 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13465 getById : function(id){
13466 return this.data.key(id);
13470 * Get the Record at the specified index.
13471 * @param {Number} index The index of the Record to find.
13472 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13474 getAt : function(index){
13475 return this.data.itemAt(index);
13479 * Returns a range of Records between specified indices.
13480 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13481 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13482 * @return {Roo.data.Record[]} An array of Records
13484 getRange : function(start, end){
13485 return this.data.getRange(start, end);
13489 storeOptions : function(o){
13490 o = Roo.apply({}, o);
13493 this.lastOptions = o;
13497 * Loads the Record cache from the configured Proxy using the configured Reader.
13499 * If using remote paging, then the first load call must specify the <em>start</em>
13500 * and <em>limit</em> properties in the options.params property to establish the initial
13501 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13503 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13504 * and this call will return before the new data has been loaded. Perform any post-processing
13505 * in a callback function, or in a "load" event handler.</strong>
13507 * @param {Object} options An object containing properties which control loading options:<ul>
13508 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13509 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13510 * passed the following arguments:<ul>
13511 * <li>r : Roo.data.Record[]</li>
13512 * <li>options: Options object from the load call</li>
13513 * <li>success: Boolean success indicator</li></ul></li>
13514 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13515 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13518 load : function(options){
13519 options = options || {};
13520 if(this.fireEvent("beforeload", this, options) !== false){
13521 this.storeOptions(options);
13522 var p = Roo.apply(options.params || {}, this.baseParams);
13523 // if meta was not loaded from remote source.. try requesting it.
13524 if (!this.reader.metaFromRemote) {
13525 p._requestMeta = 1;
13527 if(this.sortInfo && this.remoteSort){
13528 var pn = this.paramNames;
13529 p[pn["sort"]] = this.sortInfo.field;
13530 p[pn["dir"]] = this.sortInfo.direction;
13532 if (this.multiSort) {
13533 var pn = this.paramNames;
13534 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13537 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13542 * Reloads the Record cache from the configured Proxy using the configured Reader and
13543 * the options from the last load operation performed.
13544 * @param {Object} options (optional) An object containing properties which may override the options
13545 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13546 * the most recently used options are reused).
13548 reload : function(options){
13549 this.load(Roo.applyIf(options||{}, this.lastOptions));
13553 // Called as a callback by the Reader during a load operation.
13554 loadRecords : function(o, options, success){
13555 if(!o || success === false){
13556 if(success !== false){
13557 this.fireEvent("load", this, [], options, o);
13559 if(options.callback){
13560 options.callback.call(options.scope || this, [], options, false);
13564 // if data returned failure - throw an exception.
13565 if (o.success === false) {
13566 // show a message if no listener is registered.
13567 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13568 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13570 // loadmask wil be hooked into this..
13571 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13574 var r = o.records, t = o.totalRecords || r.length;
13576 this.fireEvent("beforeloadadd", this, r, options, o);
13578 if(!options || options.add !== true){
13579 if(this.pruneModifiedRecords){
13580 this.modified = [];
13582 for(var i = 0, len = r.length; i < len; i++){
13586 this.data = this.snapshot;
13587 delete this.snapshot;
13590 this.data.addAll(r);
13591 this.totalLength = t;
13593 this.fireEvent("datachanged", this);
13595 this.totalLength = Math.max(t, this.data.length+r.length);
13599 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13601 var e = new Roo.data.Record({});
13603 e.set(this.parent.displayField, this.parent.emptyTitle);
13604 e.set(this.parent.valueField, '');
13609 this.fireEvent("load", this, r, options, o);
13610 if(options.callback){
13611 options.callback.call(options.scope || this, r, options, true);
13617 * Loads data from a passed data block. A Reader which understands the format of the data
13618 * must have been configured in the constructor.
13619 * @param {Object} data The data block from which to read the Records. The format of the data expected
13620 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13621 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13623 loadData : function(o, append){
13624 var r = this.reader.readRecords(o);
13625 this.loadRecords(r, {add: append}, true);
13629 * using 'cn' the nested child reader read the child array into it's child stores.
13630 * @param {Object} rec The record with a 'children array
13632 loadDataFromChildren : function(rec)
13634 this.loadData(this.reader.toLoadData(rec));
13639 * Gets the number of cached records.
13641 * <em>If using paging, this may not be the total size of the dataset. If the data object
13642 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13643 * the data set size</em>
13645 getCount : function(){
13646 return this.data.length || 0;
13650 * Gets the total number of records in the dataset as returned by the server.
13652 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13653 * the dataset size</em>
13655 getTotalCount : function(){
13656 return this.totalLength || 0;
13660 * Returns the sort state of the Store as an object with two properties:
13662 field {String} The name of the field by which the Records are sorted
13663 direction {String} The sort order, "ASC" or "DESC"
13666 getSortState : function(){
13667 return this.sortInfo;
13671 applySort : function(){
13672 if(this.sortInfo && !this.remoteSort){
13673 var s = this.sortInfo, f = s.field;
13674 var st = this.fields.get(f).sortType;
13675 var fn = function(r1, r2){
13676 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13677 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13679 this.data.sort(s.direction, fn);
13680 if(this.snapshot && this.snapshot != this.data){
13681 this.snapshot.sort(s.direction, fn);
13687 * Sets the default sort column and order to be used by the next load operation.
13688 * @param {String} fieldName The name of the field to sort by.
13689 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13691 setDefaultSort : function(field, dir){
13692 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13696 * Sort the Records.
13697 * If remote sorting is used, the sort is performed on the server, and the cache is
13698 * reloaded. If local sorting is used, the cache is sorted internally.
13699 * @param {String} fieldName The name of the field to sort by.
13700 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13702 sort : function(fieldName, dir){
13703 var f = this.fields.get(fieldName);
13705 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13707 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13708 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13713 this.sortToggle[f.name] = dir;
13714 this.sortInfo = {field: f.name, direction: dir};
13715 if(!this.remoteSort){
13717 this.fireEvent("datachanged", this);
13719 this.load(this.lastOptions);
13724 * Calls the specified function for each of the Records in the cache.
13725 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13726 * Returning <em>false</em> aborts and exits the iteration.
13727 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13729 each : function(fn, scope){
13730 this.data.each(fn, scope);
13734 * Gets all records modified since the last commit. Modified records are persisted across load operations
13735 * (e.g., during paging).
13736 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13738 getModifiedRecords : function(){
13739 return this.modified;
13743 createFilterFn : function(property, value, anyMatch){
13744 if(!value.exec){ // not a regex
13745 value = String(value);
13746 if(value.length == 0){
13749 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13751 return function(r){
13752 return value.test(r.data[property]);
13757 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13758 * @param {String} property A field on your records
13759 * @param {Number} start The record index to start at (defaults to 0)
13760 * @param {Number} end The last record index to include (defaults to length - 1)
13761 * @return {Number} The sum
13763 sum : function(property, start, end){
13764 var rs = this.data.items, v = 0;
13765 start = start || 0;
13766 end = (end || end === 0) ? end : rs.length-1;
13768 for(var i = start; i <= end; i++){
13769 v += (rs[i].data[property] || 0);
13775 * Filter the records by a specified property.
13776 * @param {String} field A field on your records
13777 * @param {String/RegExp} value Either a string that the field
13778 * should start with or a RegExp to test against the field
13779 * @param {Boolean} anyMatch True to match any part not just the beginning
13781 filter : function(property, value, anyMatch){
13782 var fn = this.createFilterFn(property, value, anyMatch);
13783 return fn ? this.filterBy(fn) : this.clearFilter();
13787 * Filter by a function. The specified function will be called with each
13788 * record in this data source. If the function returns true the record is included,
13789 * otherwise it is filtered.
13790 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13791 * @param {Object} scope (optional) The scope of the function (defaults to this)
13793 filterBy : function(fn, scope){
13794 this.snapshot = this.snapshot || this.data;
13795 this.data = this.queryBy(fn, scope||this);
13796 this.fireEvent("datachanged", this);
13800 * Query the records by a specified property.
13801 * @param {String} field A field on your records
13802 * @param {String/RegExp} value Either a string that the field
13803 * should start with or a RegExp to test against the field
13804 * @param {Boolean} anyMatch True to match any part not just the beginning
13805 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13807 query : function(property, value, anyMatch){
13808 var fn = this.createFilterFn(property, value, anyMatch);
13809 return fn ? this.queryBy(fn) : this.data.clone();
13813 * Query by a function. The specified function will be called with each
13814 * record in this data source. If the function returns true the record is included
13816 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13817 * @param {Object} scope (optional) The scope of the function (defaults to this)
13818 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13820 queryBy : function(fn, scope){
13821 var data = this.snapshot || this.data;
13822 return data.filterBy(fn, scope||this);
13826 * Collects unique values for a particular dataIndex from this store.
13827 * @param {String} dataIndex The property to collect
13828 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13829 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13830 * @return {Array} An array of the unique values
13832 collect : function(dataIndex, allowNull, bypassFilter){
13833 var d = (bypassFilter === true && this.snapshot) ?
13834 this.snapshot.items : this.data.items;
13835 var v, sv, r = [], l = {};
13836 for(var i = 0, len = d.length; i < len; i++){
13837 v = d[i].data[dataIndex];
13839 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13848 * Revert to a view of the Record cache with no filtering applied.
13849 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13851 clearFilter : function(suppressEvent){
13852 if(this.snapshot && this.snapshot != this.data){
13853 this.data = this.snapshot;
13854 delete this.snapshot;
13855 if(suppressEvent !== true){
13856 this.fireEvent("datachanged", this);
13862 afterEdit : function(record){
13863 if(this.modified.indexOf(record) == -1){
13864 this.modified.push(record);
13866 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13870 afterReject : function(record){
13871 this.modified.remove(record);
13872 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13876 afterCommit : function(record){
13877 this.modified.remove(record);
13878 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13882 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13883 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13885 commitChanges : function(){
13886 var m = this.modified.slice(0);
13887 this.modified = [];
13888 for(var i = 0, len = m.length; i < len; i++){
13894 * Cancel outstanding changes on all changed records.
13896 rejectChanges : function(){
13897 var m = this.modified.slice(0);
13898 this.modified = [];
13899 for(var i = 0, len = m.length; i < len; i++){
13904 onMetaChange : function(meta, rtype, o){
13905 this.recordType = rtype;
13906 this.fields = rtype.prototype.fields;
13907 delete this.snapshot;
13908 this.sortInfo = meta.sortInfo || this.sortInfo;
13909 this.modified = [];
13910 this.fireEvent('metachange', this, this.reader.meta);
13913 moveIndex : function(data, type)
13915 var index = this.indexOf(data);
13917 var newIndex = index + type;
13921 this.insert(newIndex, data);
13926 * Ext JS Library 1.1.1
13927 * Copyright(c) 2006-2007, Ext JS, LLC.
13929 * Originally Released Under LGPL - original licence link has changed is not relivant.
13932 * <script type="text/javascript">
13936 * @class Roo.data.SimpleStore
13937 * @extends Roo.data.Store
13938 * Small helper class to make creating Stores from Array data easier.
13939 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13940 * @cfg {Array} fields An array of field definition objects, or field name strings.
13941 * @cfg {Object} an existing reader (eg. copied from another store)
13942 * @cfg {Array} data The multi-dimensional array of data
13944 * @param {Object} config
13946 Roo.data.SimpleStore = function(config)
13948 Roo.data.SimpleStore.superclass.constructor.call(this, {
13950 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13953 Roo.data.Record.create(config.fields)
13955 proxy : new Roo.data.MemoryProxy(config.data)
13959 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13961 * Ext JS Library 1.1.1
13962 * Copyright(c) 2006-2007, Ext JS, LLC.
13964 * Originally Released Under LGPL - original licence link has changed is not relivant.
13967 * <script type="text/javascript">
13972 * @extends Roo.data.Store
13973 * @class Roo.data.JsonStore
13974 * Small helper class to make creating Stores for JSON data easier. <br/>
13976 var store = new Roo.data.JsonStore({
13977 url: 'get-images.php',
13979 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13982 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13983 * JsonReader and HttpProxy (unless inline data is provided).</b>
13984 * @cfg {Array} fields An array of field definition objects, or field name strings.
13986 * @param {Object} config
13988 Roo.data.JsonStore = function(c){
13989 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13990 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13991 reader: new Roo.data.JsonReader(c, c.fields)
13994 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13996 * Ext JS Library 1.1.1
13997 * Copyright(c) 2006-2007, Ext JS, LLC.
13999 * Originally Released Under LGPL - original licence link has changed is not relivant.
14002 * <script type="text/javascript">
14006 Roo.data.Field = function(config){
14007 if(typeof config == "string"){
14008 config = {name: config};
14010 Roo.apply(this, config);
14013 this.type = "auto";
14016 var st = Roo.data.SortTypes;
14017 // named sortTypes are supported, here we look them up
14018 if(typeof this.sortType == "string"){
14019 this.sortType = st[this.sortType];
14022 // set default sortType for strings and dates
14023 if(!this.sortType){
14026 this.sortType = st.asUCString;
14029 this.sortType = st.asDate;
14032 this.sortType = st.none;
14037 var stripRe = /[\$,%]/g;
14039 // prebuilt conversion function for this field, instead of
14040 // switching every time we're reading a value
14042 var cv, dateFormat = this.dateFormat;
14047 cv = function(v){ return v; };
14050 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14054 return v !== undefined && v !== null && v !== '' ?
14055 parseInt(String(v).replace(stripRe, ""), 10) : '';
14060 return v !== undefined && v !== null && v !== '' ?
14061 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14066 cv = function(v){ return v === true || v === "true" || v == 1; };
14073 if(v instanceof Date){
14077 if(dateFormat == "timestamp"){
14078 return new Date(v*1000);
14080 return Date.parseDate(v, dateFormat);
14082 var parsed = Date.parse(v);
14083 return parsed ? new Date(parsed) : null;
14092 Roo.data.Field.prototype = {
14100 * Ext JS Library 1.1.1
14101 * Copyright(c) 2006-2007, Ext JS, LLC.
14103 * Originally Released Under LGPL - original licence link has changed is not relivant.
14106 * <script type="text/javascript">
14109 // Base class for reading structured data from a data source. This class is intended to be
14110 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14113 * @class Roo.data.DataReader
14114 * Base class for reading structured data from a data source. This class is intended to be
14115 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14118 Roo.data.DataReader = function(meta, recordType){
14122 this.recordType = recordType instanceof Array ?
14123 Roo.data.Record.create(recordType) : recordType;
14126 Roo.data.DataReader.prototype = {
14129 readerType : 'Data',
14131 * Create an empty record
14132 * @param {Object} data (optional) - overlay some values
14133 * @return {Roo.data.Record} record created.
14135 newRow : function(d) {
14137 this.recordType.prototype.fields.each(function(c) {
14139 case 'int' : da[c.name] = 0; break;
14140 case 'date' : da[c.name] = new Date(); break;
14141 case 'float' : da[c.name] = 0.0; break;
14142 case 'boolean' : da[c.name] = false; break;
14143 default : da[c.name] = ""; break;
14147 return new this.recordType(Roo.apply(da, d));
14153 * Ext JS Library 1.1.1
14154 * Copyright(c) 2006-2007, Ext JS, LLC.
14156 * Originally Released Under LGPL - original licence link has changed is not relivant.
14159 * <script type="text/javascript">
14163 * @class Roo.data.DataProxy
14164 * @extends Roo.data.Observable
14165 * This class is an abstract base class for implementations which provide retrieval of
14166 * unformatted data objects.<br>
14168 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14169 * (of the appropriate type which knows how to parse the data object) to provide a block of
14170 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14172 * Custom implementations must implement the load method as described in
14173 * {@link Roo.data.HttpProxy#load}.
14175 Roo.data.DataProxy = function(){
14178 * @event beforeload
14179 * Fires before a network request is made to retrieve a data object.
14180 * @param {Object} This DataProxy object.
14181 * @param {Object} params The params parameter to the load function.
14186 * Fires before the load method's callback is called.
14187 * @param {Object} This DataProxy object.
14188 * @param {Object} o The data object.
14189 * @param {Object} arg The callback argument object passed to the load function.
14193 * @event loadexception
14194 * Fires if an Exception occurs during data retrieval.
14195 * @param {Object} This DataProxy object.
14196 * @param {Object} o The data object.
14197 * @param {Object} arg The callback argument object passed to the load function.
14198 * @param {Object} e The Exception.
14200 loadexception : true
14202 Roo.data.DataProxy.superclass.constructor.call(this);
14205 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14208 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14212 * Ext JS Library 1.1.1
14213 * Copyright(c) 2006-2007, Ext JS, LLC.
14215 * Originally Released Under LGPL - original licence link has changed is not relivant.
14218 * <script type="text/javascript">
14221 * @class Roo.data.MemoryProxy
14222 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14223 * to the Reader when its load method is called.
14225 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14227 Roo.data.MemoryProxy = function(data){
14231 Roo.data.MemoryProxy.superclass.constructor.call(this);
14235 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14238 * Load data from the requested source (in this case an in-memory
14239 * data object passed to the constructor), read the data object into
14240 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14241 * process that block using the passed callback.
14242 * @param {Object} params This parameter is not used by the MemoryProxy class.
14243 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14244 * object into a block of Roo.data.Records.
14245 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14246 * The function must be passed <ul>
14247 * <li>The Record block object</li>
14248 * <li>The "arg" argument from the load function</li>
14249 * <li>A boolean success indicator</li>
14251 * @param {Object} scope The scope in which to call the callback
14252 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14254 load : function(params, reader, callback, scope, arg){
14255 params = params || {};
14258 result = reader.readRecords(params.data ? params.data :this.data);
14260 this.fireEvent("loadexception", this, arg, null, e);
14261 callback.call(scope, null, arg, false);
14264 callback.call(scope, result, arg, true);
14268 update : function(params, records){
14273 * Ext JS Library 1.1.1
14274 * Copyright(c) 2006-2007, Ext JS, LLC.
14276 * Originally Released Under LGPL - original licence link has changed is not relivant.
14279 * <script type="text/javascript">
14282 * @class Roo.data.HttpProxy
14283 * @extends Roo.data.DataProxy
14284 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14285 * configured to reference a certain URL.<br><br>
14287 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14288 * from which the running page was served.<br><br>
14290 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14292 * Be aware that to enable the browser to parse an XML document, the server must set
14293 * the Content-Type header in the HTTP response to "text/xml".
14295 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14296 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14297 * will be used to make the request.
14299 Roo.data.HttpProxy = function(conn){
14300 Roo.data.HttpProxy.superclass.constructor.call(this);
14301 // is conn a conn config or a real conn?
14303 this.useAjax = !conn || !conn.events;
14307 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14308 // thse are take from connection...
14311 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14314 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14315 * extra parameters to each request made by this object. (defaults to undefined)
14318 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14319 * to each request made by this object. (defaults to undefined)
14322 * @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)
14325 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14328 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14334 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14338 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14339 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14340 * a finer-grained basis than the DataProxy events.
14342 getConnection : function(){
14343 return this.useAjax ? Roo.Ajax : this.conn;
14347 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14348 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14349 * process that block using the passed callback.
14350 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14351 * for the request to the remote server.
14352 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14353 * object into a block of Roo.data.Records.
14354 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14355 * The function must be passed <ul>
14356 * <li>The Record block object</li>
14357 * <li>The "arg" argument from the load function</li>
14358 * <li>A boolean success indicator</li>
14360 * @param {Object} scope The scope in which to call the callback
14361 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14363 load : function(params, reader, callback, scope, arg){
14364 if(this.fireEvent("beforeload", this, params) !== false){
14366 params : params || {},
14368 callback : callback,
14373 callback : this.loadResponse,
14377 Roo.applyIf(o, this.conn);
14378 if(this.activeRequest){
14379 Roo.Ajax.abort(this.activeRequest);
14381 this.activeRequest = Roo.Ajax.request(o);
14383 this.conn.request(o);
14386 callback.call(scope||this, null, arg, false);
14391 loadResponse : function(o, success, response){
14392 delete this.activeRequest;
14394 this.fireEvent("loadexception", this, o, response);
14395 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14400 result = o.reader.read(response);
14402 this.fireEvent("loadexception", this, o, response, e);
14403 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14407 this.fireEvent("load", this, o, o.request.arg);
14408 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14412 update : function(dataSet){
14417 updateResponse : function(dataSet){
14422 * Ext JS Library 1.1.1
14423 * Copyright(c) 2006-2007, Ext JS, LLC.
14425 * Originally Released Under LGPL - original licence link has changed is not relivant.
14428 * <script type="text/javascript">
14432 * @class Roo.data.ScriptTagProxy
14433 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14434 * other than the originating domain of the running page.<br><br>
14436 * <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
14437 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14439 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14440 * source code that is used as the source inside a <script> tag.<br><br>
14442 * In order for the browser to process the returned data, the server must wrap the data object
14443 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14444 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14445 * depending on whether the callback name was passed:
14448 boolean scriptTag = false;
14449 String cb = request.getParameter("callback");
14452 response.setContentType("text/javascript");
14454 response.setContentType("application/x-json");
14456 Writer out = response.getWriter();
14458 out.write(cb + "(");
14460 out.print(dataBlock.toJsonString());
14467 * @param {Object} config A configuration object.
14469 Roo.data.ScriptTagProxy = function(config){
14470 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14471 Roo.apply(this, config);
14472 this.head = document.getElementsByTagName("head")[0];
14475 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14477 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14479 * @cfg {String} url The URL from which to request the data object.
14482 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14486 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14487 * the server the name of the callback function set up by the load call to process the returned data object.
14488 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14489 * javascript output which calls this named function passing the data object as its only parameter.
14491 callbackParam : "callback",
14493 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14494 * name to the request.
14499 * Load data from the configured URL, read the data object into
14500 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14501 * process that block using the passed callback.
14502 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14503 * for the request to the remote server.
14504 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14505 * object into a block of Roo.data.Records.
14506 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14507 * The function must be passed <ul>
14508 * <li>The Record block object</li>
14509 * <li>The "arg" argument from the load function</li>
14510 * <li>A boolean success indicator</li>
14512 * @param {Object} scope The scope in which to call the callback
14513 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14515 load : function(params, reader, callback, scope, arg){
14516 if(this.fireEvent("beforeload", this, params) !== false){
14518 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14520 var url = this.url;
14521 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14523 url += "&_dc=" + (new Date().getTime());
14525 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14528 cb : "stcCallback"+transId,
14529 scriptId : "stcScript"+transId,
14533 callback : callback,
14539 window[trans.cb] = function(o){
14540 conn.handleResponse(o, trans);
14543 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14545 if(this.autoAbort !== false){
14549 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14551 var script = document.createElement("script");
14552 script.setAttribute("src", url);
14553 script.setAttribute("type", "text/javascript");
14554 script.setAttribute("id", trans.scriptId);
14555 this.head.appendChild(script);
14557 this.trans = trans;
14559 callback.call(scope||this, null, arg, false);
14564 isLoading : function(){
14565 return this.trans ? true : false;
14569 * Abort the current server request.
14571 abort : function(){
14572 if(this.isLoading()){
14573 this.destroyTrans(this.trans);
14578 destroyTrans : function(trans, isLoaded){
14579 this.head.removeChild(document.getElementById(trans.scriptId));
14580 clearTimeout(trans.timeoutId);
14582 window[trans.cb] = undefined;
14584 delete window[trans.cb];
14587 // if hasn't been loaded, wait for load to remove it to prevent script error
14588 window[trans.cb] = function(){
14589 window[trans.cb] = undefined;
14591 delete window[trans.cb];
14598 handleResponse : function(o, trans){
14599 this.trans = false;
14600 this.destroyTrans(trans, true);
14603 result = trans.reader.readRecords(o);
14605 this.fireEvent("loadexception", this, o, trans.arg, e);
14606 trans.callback.call(trans.scope||window, null, trans.arg, false);
14609 this.fireEvent("load", this, o, trans.arg);
14610 trans.callback.call(trans.scope||window, result, trans.arg, true);
14614 handleFailure : function(trans){
14615 this.trans = false;
14616 this.destroyTrans(trans, false);
14617 this.fireEvent("loadexception", this, null, trans.arg);
14618 trans.callback.call(trans.scope||window, null, trans.arg, false);
14622 * Ext JS Library 1.1.1
14623 * Copyright(c) 2006-2007, Ext JS, LLC.
14625 * Originally Released Under LGPL - original licence link has changed is not relivant.
14628 * <script type="text/javascript">
14632 * @class Roo.data.JsonReader
14633 * @extends Roo.data.DataReader
14634 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14635 * based on mappings in a provided Roo.data.Record constructor.
14637 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14638 * in the reply previously.
14643 var RecordDef = Roo.data.Record.create([
14644 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14645 {name: 'occupation'} // This field will use "occupation" as the mapping.
14647 var myReader = new Roo.data.JsonReader({
14648 totalProperty: "results", // The property which contains the total dataset size (optional)
14649 root: "rows", // The property which contains an Array of row objects
14650 id: "id" // The property within each row object that provides an ID for the record (optional)
14654 * This would consume a JSON file like this:
14656 { 'results': 2, 'rows': [
14657 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14658 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14661 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14662 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14663 * paged from the remote server.
14664 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14665 * @cfg {String} root name of the property which contains the Array of row objects.
14666 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14667 * @cfg {Array} fields Array of field definition objects
14669 * Create a new JsonReader
14670 * @param {Object} meta Metadata configuration options
14671 * @param {Object} recordType Either an Array of field definition objects,
14672 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14674 Roo.data.JsonReader = function(meta, recordType){
14677 // set some defaults:
14678 Roo.applyIf(meta, {
14679 totalProperty: 'total',
14680 successProperty : 'success',
14685 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14687 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14689 readerType : 'Json',
14692 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14693 * Used by Store query builder to append _requestMeta to params.
14696 metaFromRemote : false,
14698 * This method is only used by a DataProxy which has retrieved data from a remote server.
14699 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14700 * @return {Object} data A data block which is used by an Roo.data.Store object as
14701 * a cache of Roo.data.Records.
14703 read : function(response){
14704 var json = response.responseText;
14706 var o = /* eval:var:o */ eval("("+json+")");
14708 throw {message: "JsonReader.read: Json object not found"};
14714 this.metaFromRemote = true;
14715 this.meta = o.metaData;
14716 this.recordType = Roo.data.Record.create(o.metaData.fields);
14717 this.onMetaChange(this.meta, this.recordType, o);
14719 return this.readRecords(o);
14722 // private function a store will implement
14723 onMetaChange : function(meta, recordType, o){
14730 simpleAccess: function(obj, subsc) {
14737 getJsonAccessor: function(){
14739 return function(expr) {
14741 return(re.test(expr))
14742 ? new Function("obj", "return obj." + expr)
14747 return Roo.emptyFn;
14752 * Create a data block containing Roo.data.Records from an XML document.
14753 * @param {Object} o An object which contains an Array of row objects in the property specified
14754 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14755 * which contains the total size of the dataset.
14756 * @return {Object} data A data block which is used by an Roo.data.Store object as
14757 * a cache of Roo.data.Records.
14759 readRecords : function(o){
14761 * After any data loads, the raw JSON data is available for further custom processing.
14765 var s = this.meta, Record = this.recordType,
14766 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14768 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14770 if(s.totalProperty) {
14771 this.getTotal = this.getJsonAccessor(s.totalProperty);
14773 if(s.successProperty) {
14774 this.getSuccess = this.getJsonAccessor(s.successProperty);
14776 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14778 var g = this.getJsonAccessor(s.id);
14779 this.getId = function(rec) {
14781 return (r === undefined || r === "") ? null : r;
14784 this.getId = function(){return null;};
14787 for(var jj = 0; jj < fl; jj++){
14789 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14790 this.ef[jj] = this.getJsonAccessor(map);
14794 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14795 if(s.totalProperty){
14796 var vt = parseInt(this.getTotal(o), 10);
14801 if(s.successProperty){
14802 var vs = this.getSuccess(o);
14803 if(vs === false || vs === 'false'){
14808 for(var i = 0; i < c; i++){
14811 var id = this.getId(n);
14812 for(var j = 0; j < fl; j++){
14814 var v = this.ef[j](n);
14816 Roo.log('missing convert for ' + f.name);
14820 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14822 var record = new Record(values, id);
14824 records[i] = record;
14830 totalRecords : totalRecords
14833 // used when loading children.. @see loadDataFromChildren
14834 toLoadData: function(rec)
14836 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14837 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14838 return { data : data, total : data.length };
14843 * Ext JS Library 1.1.1
14844 * Copyright(c) 2006-2007, Ext JS, LLC.
14846 * Originally Released Under LGPL - original licence link has changed is not relivant.
14849 * <script type="text/javascript">
14853 * @class Roo.data.ArrayReader
14854 * @extends Roo.data.DataReader
14855 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14856 * Each element of that Array represents a row of data fields. The
14857 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14858 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14862 var RecordDef = Roo.data.Record.create([
14863 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14864 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14866 var myReader = new Roo.data.ArrayReader({
14867 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14871 * This would consume an Array like this:
14873 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14877 * Create a new JsonReader
14878 * @param {Object} meta Metadata configuration options.
14879 * @param {Object|Array} recordType Either an Array of field definition objects
14881 * @cfg {Array} fields Array of field definition objects
14882 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14883 * as specified to {@link Roo.data.Record#create},
14884 * or an {@link Roo.data.Record} object
14887 * created using {@link Roo.data.Record#create}.
14889 Roo.data.ArrayReader = function(meta, recordType)
14891 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14894 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14897 * Create a data block containing Roo.data.Records from an XML document.
14898 * @param {Object} o An Array of row objects which represents the dataset.
14899 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14900 * a cache of Roo.data.Records.
14902 readRecords : function(o)
14904 var sid = this.meta ? this.meta.id : null;
14905 var recordType = this.recordType, fields = recordType.prototype.fields;
14908 for(var i = 0; i < root.length; i++){
14911 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14912 for(var j = 0, jlen = fields.length; j < jlen; j++){
14913 var f = fields.items[j];
14914 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14915 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14917 values[f.name] = v;
14919 var record = new recordType(values, id);
14921 records[records.length] = record;
14925 totalRecords : records.length
14928 // used when loading children.. @see loadDataFromChildren
14929 toLoadData: function(rec)
14931 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14932 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14943 * @class Roo.bootstrap.ComboBox
14944 * @extends Roo.bootstrap.TriggerField
14945 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14946 * @cfg {Boolean} append (true|false) default false
14947 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14948 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14949 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14950 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14951 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14952 * @cfg {Boolean} animate default true
14953 * @cfg {Boolean} emptyResultText only for touch device
14954 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14955 * @cfg {String} emptyTitle default ''
14956 * @cfg {Number} width fixed with? experimental
14958 * Create a new ComboBox.
14959 * @param {Object} config Configuration options
14961 Roo.bootstrap.ComboBox = function(config){
14962 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14966 * Fires when the dropdown list is expanded
14967 * @param {Roo.bootstrap.ComboBox} combo This combo box
14972 * Fires when the dropdown list is collapsed
14973 * @param {Roo.bootstrap.ComboBox} combo This combo box
14977 * @event beforeselect
14978 * Fires before a list item is selected. Return false to cancel the selection.
14979 * @param {Roo.bootstrap.ComboBox} combo This combo box
14980 * @param {Roo.data.Record} record The data record returned from the underlying store
14981 * @param {Number} index The index of the selected item in the dropdown list
14983 'beforeselect' : true,
14986 * Fires when a list item is selected
14987 * @param {Roo.bootstrap.ComboBox} combo This combo box
14988 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14989 * @param {Number} index The index of the selected item in the dropdown list
14993 * @event beforequery
14994 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14995 * The event object passed has these properties:
14996 * @param {Roo.bootstrap.ComboBox} combo This combo box
14997 * @param {String} query The query
14998 * @param {Boolean} forceAll true to force "all" query
14999 * @param {Boolean} cancel true to cancel the query
15000 * @param {Object} e The query event object
15002 'beforequery': true,
15005 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15006 * @param {Roo.bootstrap.ComboBox} combo This combo box
15011 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15012 * @param {Roo.bootstrap.ComboBox} combo This combo box
15013 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15018 * Fires when the remove value from the combobox array
15019 * @param {Roo.bootstrap.ComboBox} combo This combo box
15023 * @event afterremove
15024 * Fires when the remove value from the combobox array
15025 * @param {Roo.bootstrap.ComboBox} combo This combo box
15027 'afterremove' : true,
15029 * @event specialfilter
15030 * Fires when specialfilter
15031 * @param {Roo.bootstrap.ComboBox} combo This combo box
15033 'specialfilter' : true,
15036 * Fires when tick the element
15037 * @param {Roo.bootstrap.ComboBox} combo This combo box
15041 * @event touchviewdisplay
15042 * Fires when touch view require special display (default is using displayField)
15043 * @param {Roo.bootstrap.ComboBox} combo This combo box
15044 * @param {Object} cfg set html .
15046 'touchviewdisplay' : true
15051 this.tickItems = [];
15053 this.selectedIndex = -1;
15054 if(this.mode == 'local'){
15055 if(config.queryDelay === undefined){
15056 this.queryDelay = 10;
15058 if(config.minChars === undefined){
15064 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15067 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15068 * rendering into an Roo.Editor, defaults to false)
15071 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15072 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15075 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15078 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15079 * the dropdown list (defaults to undefined, with no header element)
15083 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15087 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15089 listWidth: undefined,
15091 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15092 * mode = 'remote' or 'text' if mode = 'local')
15094 displayField: undefined,
15097 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15098 * mode = 'remote' or 'value' if mode = 'local').
15099 * Note: use of a valueField requires the user make a selection
15100 * in order for a value to be mapped.
15102 valueField: undefined,
15104 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15109 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15110 * field's data value (defaults to the underlying DOM element's name)
15112 hiddenName: undefined,
15114 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15118 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15120 selectedClass: 'active',
15123 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15127 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15128 * anchor positions (defaults to 'tl-bl')
15130 listAlign: 'tl-bl?',
15132 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15136 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15137 * query specified by the allQuery config option (defaults to 'query')
15139 triggerAction: 'query',
15141 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15142 * (defaults to 4, does not apply if editable = false)
15146 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15147 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15151 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15152 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15156 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15157 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15161 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15162 * when editable = true (defaults to false)
15164 selectOnFocus:false,
15166 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15168 queryParam: 'query',
15170 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15171 * when mode = 'remote' (defaults to 'Loading...')
15173 loadingText: 'Loading...',
15175 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15179 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15183 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15184 * traditional select (defaults to true)
15188 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15192 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15196 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15197 * listWidth has a higher value)
15201 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15202 * allow the user to set arbitrary text into the field (defaults to false)
15204 forceSelection:false,
15206 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15207 * if typeAhead = true (defaults to 250)
15209 typeAheadDelay : 250,
15211 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15212 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15214 valueNotFoundText : undefined,
15216 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15218 blockFocus : false,
15221 * @cfg {Boolean} disableClear Disable showing of clear button.
15223 disableClear : false,
15225 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15227 alwaysQuery : false,
15230 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15235 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15237 invalidClass : "has-warning",
15240 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15242 validClass : "has-success",
15245 * @cfg {Boolean} specialFilter (true|false) special filter default false
15247 specialFilter : false,
15250 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15252 mobileTouchView : true,
15255 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15257 useNativeIOS : false,
15260 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15262 mobile_restrict_height : false,
15264 ios_options : false,
15276 btnPosition : 'right',
15277 triggerList : true,
15278 showToggleBtn : true,
15280 emptyResultText: 'Empty',
15281 triggerText : 'Select',
15285 // element that contains real text value.. (when hidden is used..)
15287 getAutoCreate : function()
15292 * Render classic select for iso
15295 if(Roo.isIOS && this.useNativeIOS){
15296 cfg = this.getAutoCreateNativeIOS();
15304 if(Roo.isTouch && this.mobileTouchView){
15305 cfg = this.getAutoCreateTouchView();
15312 if(!this.tickable){
15313 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15318 * ComboBox with tickable selections
15321 var align = this.labelAlign || this.parentLabelAlign();
15324 cls : 'form-group roo-combobox-tickable' //input-group
15327 var btn_text_select = '';
15328 var btn_text_done = '';
15329 var btn_text_cancel = '';
15331 if (this.btn_text_show) {
15332 btn_text_select = 'Select';
15333 btn_text_done = 'Done';
15334 btn_text_cancel = 'Cancel';
15339 cls : 'tickable-buttons',
15344 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15345 //html : this.triggerText
15346 html: btn_text_select
15352 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15354 html: btn_text_done
15360 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15362 html: btn_text_cancel
15368 buttons.cn.unshift({
15370 cls: 'roo-select2-search-field-input'
15376 Roo.each(buttons.cn, function(c){
15378 c.cls += ' btn-' + _this.size;
15381 if (_this.disabled) {
15388 style : 'display: contents',
15393 cls: 'form-hidden-field'
15397 cls: 'roo-select2-choices',
15401 cls: 'roo-select2-search-field',
15412 cls: 'roo-select2-container input-group roo-select2-container-multi',
15418 // cls: 'typeahead typeahead-long dropdown-menu',
15419 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15424 if(this.hasFeedback && !this.allowBlank){
15428 cls: 'glyphicon form-control-feedback'
15431 combobox.cn.push(feedback);
15438 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15439 tooltip : 'This field is required'
15441 if (Roo.bootstrap.version == 4) {
15444 style : 'display:none'
15447 if (align ==='left' && this.fieldLabel.length) {
15449 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15456 cls : 'control-label col-form-label',
15457 html : this.fieldLabel
15469 var labelCfg = cfg.cn[1];
15470 var contentCfg = cfg.cn[2];
15473 if(this.indicatorpos == 'right'){
15479 cls : 'control-label col-form-label',
15483 html : this.fieldLabel
15499 labelCfg = cfg.cn[0];
15500 contentCfg = cfg.cn[1];
15504 if(this.labelWidth > 12){
15505 labelCfg.style = "width: " + this.labelWidth + 'px';
15507 if(this.width * 1 > 0){
15508 contentCfg.style = "width: " + this.width + 'px';
15510 if(this.labelWidth < 13 && this.labelmd == 0){
15511 this.labelmd = this.labelWidth;
15514 if(this.labellg > 0){
15515 labelCfg.cls += ' col-lg-' + this.labellg;
15516 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15519 if(this.labelmd > 0){
15520 labelCfg.cls += ' col-md-' + this.labelmd;
15521 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15524 if(this.labelsm > 0){
15525 labelCfg.cls += ' col-sm-' + this.labelsm;
15526 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15529 if(this.labelxs > 0){
15530 labelCfg.cls += ' col-xs-' + this.labelxs;
15531 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15535 } else if ( this.fieldLabel.length) {
15536 // Roo.log(" label");
15541 //cls : 'input-group-addon',
15542 html : this.fieldLabel
15547 if(this.indicatorpos == 'right'){
15551 //cls : 'input-group-addon',
15552 html : this.fieldLabel
15562 // Roo.log(" no label && no align");
15569 ['xs','sm','md','lg'].map(function(size){
15570 if (settings[size]) {
15571 cfg.cls += ' col-' + size + '-' + settings[size];
15579 _initEventsCalled : false,
15582 initEvents: function()
15584 if (this._initEventsCalled) { // as we call render... prevent looping...
15587 this._initEventsCalled = true;
15590 throw "can not find store for combo";
15593 this.indicator = this.indicatorEl();
15595 this.store = Roo.factory(this.store, Roo.data);
15596 this.store.parent = this;
15598 // if we are building from html. then this element is so complex, that we can not really
15599 // use the rendered HTML.
15600 // so we have to trash and replace the previous code.
15601 if (Roo.XComponent.build_from_html) {
15602 // remove this element....
15603 var e = this.el.dom, k=0;
15604 while (e ) { e = e.previousSibling; ++k;}
15609 this.rendered = false;
15611 this.render(this.parent().getChildContainer(true), k);
15614 if(Roo.isIOS && this.useNativeIOS){
15615 this.initIOSView();
15623 if(Roo.isTouch && this.mobileTouchView){
15624 this.initTouchView();
15629 this.initTickableEvents();
15633 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15635 if(this.hiddenName){
15637 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15639 this.hiddenField.dom.value =
15640 this.hiddenValue !== undefined ? this.hiddenValue :
15641 this.value !== undefined ? this.value : '';
15643 // prevent input submission
15644 this.el.dom.removeAttribute('name');
15645 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15650 // this.el.dom.setAttribute('autocomplete', 'off');
15653 var cls = 'x-combo-list';
15655 //this.list = new Roo.Layer({
15656 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15662 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15663 _this.list.setWidth(lw);
15666 this.list.on('mouseover', this.onViewOver, this);
15667 this.list.on('mousemove', this.onViewMove, this);
15668 this.list.on('scroll', this.onViewScroll, this);
15671 this.list.swallowEvent('mousewheel');
15672 this.assetHeight = 0;
15675 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15676 this.assetHeight += this.header.getHeight();
15679 this.innerList = this.list.createChild({cls:cls+'-inner'});
15680 this.innerList.on('mouseover', this.onViewOver, this);
15681 this.innerList.on('mousemove', this.onViewMove, this);
15682 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15684 if(this.allowBlank && !this.pageSize && !this.disableClear){
15685 this.footer = this.list.createChild({cls:cls+'-ft'});
15686 this.pageTb = new Roo.Toolbar(this.footer);
15690 this.footer = this.list.createChild({cls:cls+'-ft'});
15691 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15692 {pageSize: this.pageSize});
15696 if (this.pageTb && this.allowBlank && !this.disableClear) {
15698 this.pageTb.add(new Roo.Toolbar.Fill(), {
15699 cls: 'x-btn-icon x-btn-clear',
15701 handler: function()
15704 _this.clearValue();
15705 _this.onSelect(false, -1);
15710 this.assetHeight += this.footer.getHeight();
15715 this.tpl = Roo.bootstrap.version == 4 ?
15716 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15717 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15720 this.view = new Roo.View(this.list, this.tpl, {
15721 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15723 //this.view.wrapEl.setDisplayed(false);
15724 this.view.on('click', this.onViewClick, this);
15727 this.store.on('beforeload', this.onBeforeLoad, this);
15728 this.store.on('load', this.onLoad, this);
15729 this.store.on('loadexception', this.onLoadException, this);
15731 if(this.resizable){
15732 this.resizer = new Roo.Resizable(this.list, {
15733 pinned:true, handles:'se'
15735 this.resizer.on('resize', function(r, w, h){
15736 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15737 this.listWidth = w;
15738 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15739 this.restrictHeight();
15741 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15744 if(!this.editable){
15745 this.editable = true;
15746 this.setEditable(false);
15751 if (typeof(this.events.add.listeners) != 'undefined') {
15753 this.addicon = this.wrap.createChild(
15754 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15756 this.addicon.on('click', function(e) {
15757 this.fireEvent('add', this);
15760 if (typeof(this.events.edit.listeners) != 'undefined') {
15762 this.editicon = this.wrap.createChild(
15763 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15764 if (this.addicon) {
15765 this.editicon.setStyle('margin-left', '40px');
15767 this.editicon.on('click', function(e) {
15769 // we fire even if inothing is selected..
15770 this.fireEvent('edit', this, this.lastData );
15776 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15777 "up" : function(e){
15778 this.inKeyMode = true;
15782 "down" : function(e){
15783 if(!this.isExpanded()){
15784 this.onTriggerClick();
15786 this.inKeyMode = true;
15791 "enter" : function(e){
15792 // this.onViewClick();
15796 if(this.fireEvent("specialkey", this, e)){
15797 this.onViewClick(false);
15803 "esc" : function(e){
15807 "tab" : function(e){
15810 if(this.fireEvent("specialkey", this, e)){
15811 this.onViewClick(false);
15819 doRelay : function(foo, bar, hname){
15820 if(hname == 'down' || this.scope.isExpanded()){
15821 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15830 this.queryDelay = Math.max(this.queryDelay || 10,
15831 this.mode == 'local' ? 10 : 250);
15834 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15836 if(this.typeAhead){
15837 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15839 if(this.editable !== false){
15840 this.inputEl().on("keyup", this.onKeyUp, this);
15842 if(this.forceSelection){
15843 this.inputEl().on('blur', this.doForce, this);
15847 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15848 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15852 initTickableEvents: function()
15856 if(this.hiddenName){
15858 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15860 this.hiddenField.dom.value =
15861 this.hiddenValue !== undefined ? this.hiddenValue :
15862 this.value !== undefined ? this.value : '';
15864 // prevent input submission
15865 this.el.dom.removeAttribute('name');
15866 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15871 // this.list = this.el.select('ul.dropdown-menu',true).first();
15873 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15874 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15875 if(this.triggerList){
15876 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15879 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15880 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15882 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15883 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15885 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15886 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15888 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15889 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15890 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15893 this.cancelBtn.hide();
15898 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15899 _this.list.setWidth(lw);
15902 this.list.on('mouseover', this.onViewOver, this);
15903 this.list.on('mousemove', this.onViewMove, this);
15905 this.list.on('scroll', this.onViewScroll, this);
15908 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15909 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15912 this.view = new Roo.View(this.list, this.tpl, {
15917 selectedClass: this.selectedClass
15920 //this.view.wrapEl.setDisplayed(false);
15921 this.view.on('click', this.onViewClick, this);
15925 this.store.on('beforeload', this.onBeforeLoad, this);
15926 this.store.on('load', this.onLoad, this);
15927 this.store.on('loadexception', this.onLoadException, this);
15930 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15931 "up" : function(e){
15932 this.inKeyMode = true;
15936 "down" : function(e){
15937 this.inKeyMode = true;
15941 "enter" : function(e){
15942 if(this.fireEvent("specialkey", this, e)){
15943 this.onViewClick(false);
15949 "esc" : function(e){
15950 this.onTickableFooterButtonClick(e, false, false);
15953 "tab" : function(e){
15954 this.fireEvent("specialkey", this, e);
15956 this.onTickableFooterButtonClick(e, false, false);
15963 doRelay : function(e, fn, key){
15964 if(this.scope.isExpanded()){
15965 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15974 this.queryDelay = Math.max(this.queryDelay || 10,
15975 this.mode == 'local' ? 10 : 250);
15978 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15980 if(this.typeAhead){
15981 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15984 if(this.editable !== false){
15985 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15988 this.indicator = this.indicatorEl();
15990 if(this.indicator){
15991 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15992 this.indicator.hide();
15997 onDestroy : function(){
15999 this.view.setStore(null);
16000 this.view.el.removeAllListeners();
16001 this.view.el.remove();
16002 this.view.purgeListeners();
16005 this.list.dom.innerHTML = '';
16009 this.store.un('beforeload', this.onBeforeLoad, this);
16010 this.store.un('load', this.onLoad, this);
16011 this.store.un('loadexception', this.onLoadException, this);
16013 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16017 fireKey : function(e){
16018 if(e.isNavKeyPress() && !this.list.isVisible()){
16019 this.fireEvent("specialkey", this, e);
16024 onResize: function(w, h)
16028 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16030 // if(typeof w != 'number'){
16031 // // we do not handle it!?!?
16034 // var tw = this.trigger.getWidth();
16035 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16036 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16038 // this.inputEl().setWidth( this.adjustWidth('input', x));
16040 // //this.trigger.setStyle('left', x+'px');
16042 // if(this.list && this.listWidth === undefined){
16043 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16044 // this.list.setWidth(lw);
16045 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16053 * Allow or prevent the user from directly editing the field text. If false is passed,
16054 * the user will only be able to select from the items defined in the dropdown list. This method
16055 * is the runtime equivalent of setting the 'editable' config option at config time.
16056 * @param {Boolean} value True to allow the user to directly edit the field text
16058 setEditable : function(value){
16059 if(value == this.editable){
16062 this.editable = value;
16064 this.inputEl().dom.setAttribute('readOnly', true);
16065 this.inputEl().on('mousedown', this.onTriggerClick, this);
16066 this.inputEl().addClass('x-combo-noedit');
16068 this.inputEl().dom.setAttribute('readOnly', false);
16069 this.inputEl().un('mousedown', this.onTriggerClick, this);
16070 this.inputEl().removeClass('x-combo-noedit');
16076 onBeforeLoad : function(combo,opts){
16077 if(!this.hasFocus){
16081 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16083 this.restrictHeight();
16084 this.selectedIndex = -1;
16088 onLoad : function(){
16090 this.hasQuery = false;
16092 if(!this.hasFocus){
16096 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16097 this.loading.hide();
16100 if(this.store.getCount() > 0){
16103 this.restrictHeight();
16104 if(this.lastQuery == this.allQuery){
16105 if(this.editable && !this.tickable){
16106 this.inputEl().dom.select();
16110 !this.selectByValue(this.value, true) &&
16113 !this.store.lastOptions ||
16114 typeof(this.store.lastOptions.add) == 'undefined' ||
16115 this.store.lastOptions.add != true
16118 this.select(0, true);
16121 if(this.autoFocus){
16124 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16125 this.taTask.delay(this.typeAheadDelay);
16129 this.onEmptyResults();
16135 onLoadException : function()
16137 this.hasQuery = false;
16139 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16140 this.loading.hide();
16143 if(this.tickable && this.editable){
16148 // only causes errors at present
16149 //Roo.log(this.store.reader.jsonData);
16150 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16152 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16158 onTypeAhead : function(){
16159 if(this.store.getCount() > 0){
16160 var r = this.store.getAt(0);
16161 var newValue = r.data[this.displayField];
16162 var len = newValue.length;
16163 var selStart = this.getRawValue().length;
16165 if(selStart != len){
16166 this.setRawValue(newValue);
16167 this.selectText(selStart, newValue.length);
16173 onSelect : function(record, index){
16175 if(this.fireEvent('beforeselect', this, record, index) !== false){
16177 this.setFromData(index > -1 ? record.data : false);
16180 this.fireEvent('select', this, record, index);
16185 * Returns the currently selected field value or empty string if no value is set.
16186 * @return {String} value The selected value
16188 getValue : function()
16190 if(Roo.isIOS && this.useNativeIOS){
16191 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16195 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16198 if(this.valueField){
16199 return typeof this.value != 'undefined' ? this.value : '';
16201 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16205 getRawValue : function()
16207 if(Roo.isIOS && this.useNativeIOS){
16208 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16211 var v = this.inputEl().getValue();
16217 * Clears any text/value currently set in the field
16219 clearValue : function(){
16221 if(this.hiddenField){
16222 this.hiddenField.dom.value = '';
16225 this.setRawValue('');
16226 this.lastSelectionText = '';
16227 this.lastData = false;
16229 var close = this.closeTriggerEl();
16240 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16241 * will be displayed in the field. If the value does not match the data value of an existing item,
16242 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16243 * Otherwise the field will be blank (although the value will still be set).
16244 * @param {String} value The value to match
16246 setValue : function(v)
16248 if(Roo.isIOS && this.useNativeIOS){
16249 this.setIOSValue(v);
16259 if(this.valueField){
16260 var r = this.findRecord(this.valueField, v);
16262 text = r.data[this.displayField];
16263 }else if(this.valueNotFoundText !== undefined){
16264 text = this.valueNotFoundText;
16267 this.lastSelectionText = text;
16268 if(this.hiddenField){
16269 this.hiddenField.dom.value = v;
16271 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16274 var close = this.closeTriggerEl();
16277 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16283 * @property {Object} the last set data for the element
16288 * Sets the value of the field based on a object which is related to the record format for the store.
16289 * @param {Object} value the value to set as. or false on reset?
16291 setFromData : function(o){
16298 var dv = ''; // display value
16299 var vv = ''; // value value..
16301 if (this.displayField) {
16302 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16304 // this is an error condition!!!
16305 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16308 if(this.valueField){
16309 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16312 var close = this.closeTriggerEl();
16315 if(dv.length || vv * 1 > 0){
16317 this.blockFocus=true;
16323 if(this.hiddenField){
16324 this.hiddenField.dom.value = vv;
16326 this.lastSelectionText = dv;
16327 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16331 // no hidden field.. - we store the value in 'value', but still display
16332 // display field!!!!
16333 this.lastSelectionText = dv;
16334 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16341 reset : function(){
16342 // overridden so that last data is reset..
16349 this.setValue(this.originalValue);
16350 //this.clearInvalid();
16351 this.lastData = false;
16353 this.view.clearSelections();
16359 findRecord : function(prop, value){
16361 if(this.store.getCount() > 0){
16362 this.store.each(function(r){
16363 if(r.data[prop] == value){
16373 getName: function()
16375 // returns hidden if it's set..
16376 if (!this.rendered) {return ''};
16377 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16381 onViewMove : function(e, t){
16382 this.inKeyMode = false;
16386 onViewOver : function(e, t){
16387 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16390 var item = this.view.findItemFromChild(t);
16393 var index = this.view.indexOf(item);
16394 this.select(index, false);
16399 onViewClick : function(view, doFocus, el, e)
16401 var index = this.view.getSelectedIndexes()[0];
16403 var r = this.store.getAt(index);
16407 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16414 Roo.each(this.tickItems, function(v,k){
16416 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16418 _this.tickItems.splice(k, 1);
16420 if(typeof(e) == 'undefined' && view == false){
16421 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16433 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16434 this.tickItems.push(r.data);
16437 if(typeof(e) == 'undefined' && view == false){
16438 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16445 this.onSelect(r, index);
16447 if(doFocus !== false && !this.blockFocus){
16448 this.inputEl().focus();
16453 restrictHeight : function(){
16454 //this.innerList.dom.style.height = '';
16455 //var inner = this.innerList.dom;
16456 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16457 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16458 //this.list.beginUpdate();
16459 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16460 this.list.alignTo(this.inputEl(), this.listAlign);
16461 this.list.alignTo(this.inputEl(), this.listAlign);
16462 //this.list.endUpdate();
16466 onEmptyResults : function(){
16468 if(this.tickable && this.editable){
16469 this.hasFocus = false;
16470 this.restrictHeight();
16478 * Returns true if the dropdown list is expanded, else false.
16480 isExpanded : function(){
16481 return this.list.isVisible();
16485 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16486 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16487 * @param {String} value The data value of the item to select
16488 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16489 * selected item if it is not currently in view (defaults to true)
16490 * @return {Boolean} True if the value matched an item in the list, else false
16492 selectByValue : function(v, scrollIntoView){
16493 if(v !== undefined && v !== null){
16494 var r = this.findRecord(this.valueField || this.displayField, v);
16496 this.select(this.store.indexOf(r), scrollIntoView);
16504 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16505 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16506 * @param {Number} index The zero-based index of the list item to select
16507 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16508 * selected item if it is not currently in view (defaults to true)
16510 select : function(index, scrollIntoView){
16511 this.selectedIndex = index;
16512 this.view.select(index);
16513 if(scrollIntoView !== false){
16514 var el = this.view.getNode(index);
16516 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16519 this.list.scrollChildIntoView(el, false);
16525 selectNext : function(){
16526 var ct = this.store.getCount();
16528 if(this.selectedIndex == -1){
16530 }else if(this.selectedIndex < ct-1){
16531 this.select(this.selectedIndex+1);
16537 selectPrev : function(){
16538 var ct = this.store.getCount();
16540 if(this.selectedIndex == -1){
16542 }else if(this.selectedIndex != 0){
16543 this.select(this.selectedIndex-1);
16549 onKeyUp : function(e){
16550 if(this.editable !== false && !e.isSpecialKey()){
16551 this.lastKey = e.getKey();
16552 this.dqTask.delay(this.queryDelay);
16557 validateBlur : function(){
16558 return !this.list || !this.list.isVisible();
16562 initQuery : function(){
16564 var v = this.getRawValue();
16566 if(this.tickable && this.editable){
16567 v = this.tickableInputEl().getValue();
16574 doForce : function(){
16575 if(this.inputEl().dom.value.length > 0){
16576 this.inputEl().dom.value =
16577 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16583 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16584 * query allowing the query action to be canceled if needed.
16585 * @param {String} query The SQL query to execute
16586 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16587 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16588 * saved in the current store (defaults to false)
16590 doQuery : function(q, forceAll){
16592 if(q === undefined || q === null){
16597 forceAll: forceAll,
16601 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16606 forceAll = qe.forceAll;
16607 if(forceAll === true || (q.length >= this.minChars)){
16609 this.hasQuery = true;
16611 if(this.lastQuery != q || this.alwaysQuery){
16612 this.lastQuery = q;
16613 if(this.mode == 'local'){
16614 this.selectedIndex = -1;
16616 this.store.clearFilter();
16619 if(this.specialFilter){
16620 this.fireEvent('specialfilter', this);
16625 this.store.filter(this.displayField, q);
16628 this.store.fireEvent("datachanged", this.store);
16635 this.store.baseParams[this.queryParam] = q;
16637 var options = {params : this.getParams(q)};
16640 options.add = true;
16641 options.params.start = this.page * this.pageSize;
16644 this.store.load(options);
16647 * this code will make the page width larger, at the beginning, the list not align correctly,
16648 * we should expand the list on onLoad
16649 * so command out it
16654 this.selectedIndex = -1;
16659 this.loadNext = false;
16663 getParams : function(q){
16665 //p[this.queryParam] = q;
16669 p.limit = this.pageSize;
16675 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16677 collapse : function(){
16678 if(!this.isExpanded()){
16684 this.hasFocus = false;
16688 this.cancelBtn.hide();
16689 this.trigger.show();
16692 this.tickableInputEl().dom.value = '';
16693 this.tickableInputEl().blur();
16698 Roo.get(document).un('mousedown', this.collapseIf, this);
16699 Roo.get(document).un('mousewheel', this.collapseIf, this);
16700 if (!this.editable) {
16701 Roo.get(document).un('keydown', this.listKeyPress, this);
16703 this.fireEvent('collapse', this);
16709 collapseIf : function(e){
16710 var in_combo = e.within(this.el);
16711 var in_list = e.within(this.list);
16712 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16714 if (in_combo || in_list || is_list) {
16715 //e.stopPropagation();
16720 this.onTickableFooterButtonClick(e, false, false);
16728 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16730 expand : function(){
16732 if(this.isExpanded() || !this.hasFocus){
16736 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16737 this.list.setWidth(lw);
16743 this.restrictHeight();
16747 this.tickItems = Roo.apply([], this.item);
16750 this.cancelBtn.show();
16751 this.trigger.hide();
16754 this.tickableInputEl().focus();
16759 Roo.get(document).on('mousedown', this.collapseIf, this);
16760 Roo.get(document).on('mousewheel', this.collapseIf, this);
16761 if (!this.editable) {
16762 Roo.get(document).on('keydown', this.listKeyPress, this);
16765 this.fireEvent('expand', this);
16769 // Implements the default empty TriggerField.onTriggerClick function
16770 onTriggerClick : function(e)
16772 Roo.log('trigger click');
16774 if(this.disabled || !this.triggerList){
16779 this.loadNext = false;
16781 if(this.isExpanded()){
16783 if (!this.blockFocus) {
16784 this.inputEl().focus();
16788 this.hasFocus = true;
16789 if(this.triggerAction == 'all') {
16790 this.doQuery(this.allQuery, true);
16792 this.doQuery(this.getRawValue());
16794 if (!this.blockFocus) {
16795 this.inputEl().focus();
16800 onTickableTriggerClick : function(e)
16807 this.loadNext = false;
16808 this.hasFocus = true;
16810 if(this.triggerAction == 'all') {
16811 this.doQuery(this.allQuery, true);
16813 this.doQuery(this.getRawValue());
16817 onSearchFieldClick : function(e)
16819 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16820 this.onTickableFooterButtonClick(e, false, false);
16824 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16829 this.loadNext = false;
16830 this.hasFocus = true;
16832 if(this.triggerAction == 'all') {
16833 this.doQuery(this.allQuery, true);
16835 this.doQuery(this.getRawValue());
16839 listKeyPress : function(e)
16841 //Roo.log('listkeypress');
16842 // scroll to first matching element based on key pres..
16843 if (e.isSpecialKey()) {
16846 var k = String.fromCharCode(e.getKey()).toUpperCase();
16849 var csel = this.view.getSelectedNodes();
16850 var cselitem = false;
16852 var ix = this.view.indexOf(csel[0]);
16853 cselitem = this.store.getAt(ix);
16854 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16860 this.store.each(function(v) {
16862 // start at existing selection.
16863 if (cselitem.id == v.id) {
16869 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16870 match = this.store.indexOf(v);
16876 if (match === false) {
16877 return true; // no more action?
16880 this.view.select(match);
16881 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16882 sn.scrollIntoView(sn.dom.parentNode, false);
16885 onViewScroll : function(e, t){
16887 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){
16891 this.hasQuery = true;
16893 this.loading = this.list.select('.loading', true).first();
16895 if(this.loading === null){
16896 this.list.createChild({
16898 cls: 'loading roo-select2-more-results roo-select2-active',
16899 html: 'Loading more results...'
16902 this.loading = this.list.select('.loading', true).first();
16904 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16906 this.loading.hide();
16909 this.loading.show();
16914 this.loadNext = true;
16916 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16921 addItem : function(o)
16923 var dv = ''; // display value
16925 if (this.displayField) {
16926 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16928 // this is an error condition!!!
16929 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16936 var choice = this.choices.createChild({
16938 cls: 'roo-select2-search-choice',
16947 cls: 'roo-select2-search-choice-close fa fa-times',
16952 }, this.searchField);
16954 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16956 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16964 this.inputEl().dom.value = '';
16969 onRemoveItem : function(e, _self, o)
16971 e.preventDefault();
16973 this.lastItem = Roo.apply([], this.item);
16975 var index = this.item.indexOf(o.data) * 1;
16978 Roo.log('not this item?!');
16982 this.item.splice(index, 1);
16987 this.fireEvent('remove', this, e);
16993 syncValue : function()
16995 if(!this.item.length){
17002 Roo.each(this.item, function(i){
17003 if(_this.valueField){
17004 value.push(i[_this.valueField]);
17011 this.value = value.join(',');
17013 if(this.hiddenField){
17014 this.hiddenField.dom.value = this.value;
17017 this.store.fireEvent("datachanged", this.store);
17022 clearItem : function()
17024 if(!this.multiple){
17030 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17038 if(this.tickable && !Roo.isTouch){
17039 this.view.refresh();
17043 inputEl: function ()
17045 if(Roo.isIOS && this.useNativeIOS){
17046 return this.el.select('select.roo-ios-select', true).first();
17049 if(Roo.isTouch && this.mobileTouchView){
17050 return this.el.select('input.form-control',true).first();
17054 return this.searchField;
17057 return this.el.select('input.form-control',true).first();
17060 onTickableFooterButtonClick : function(e, btn, el)
17062 e.preventDefault();
17064 this.lastItem = Roo.apply([], this.item);
17066 if(btn && btn.name == 'cancel'){
17067 this.tickItems = Roo.apply([], this.item);
17076 Roo.each(this.tickItems, function(o){
17084 validate : function()
17086 if(this.getVisibilityEl().hasClass('hidden')){
17090 var v = this.getRawValue();
17093 v = this.getValue();
17096 if(this.disabled || this.allowBlank || v.length){
17101 this.markInvalid();
17105 tickableInputEl : function()
17107 if(!this.tickable || !this.editable){
17108 return this.inputEl();
17111 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17115 getAutoCreateTouchView : function()
17120 cls: 'form-group' //input-group
17126 type : this.inputType,
17127 cls : 'form-control x-combo-noedit',
17128 autocomplete: 'new-password',
17129 placeholder : this.placeholder || '',
17134 input.name = this.name;
17138 input.cls += ' input-' + this.size;
17141 if (this.disabled) {
17142 input.disabled = true;
17146 cls : 'roo-combobox-wrap',
17153 inputblock.cls += ' input-group';
17155 inputblock.cn.unshift({
17157 cls : 'input-group-addon input-group-prepend input-group-text',
17162 if(this.removable && !this.multiple){
17163 inputblock.cls += ' roo-removable';
17165 inputblock.cn.push({
17168 cls : 'roo-combo-removable-btn close'
17172 if(this.hasFeedback && !this.allowBlank){
17174 inputblock.cls += ' has-feedback';
17176 inputblock.cn.push({
17178 cls: 'glyphicon form-control-feedback'
17185 inputblock.cls += (this.before) ? '' : ' input-group';
17187 inputblock.cn.push({
17189 cls : 'input-group-addon input-group-append input-group-text',
17195 var ibwrap = inputblock;
17200 cls: 'roo-select2-choices',
17204 cls: 'roo-select2-search-field',
17217 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17222 cls: 'form-hidden-field'
17228 if(!this.multiple && this.showToggleBtn){
17234 if (this.caret != false) {
17237 cls: 'fa fa-' + this.caret
17244 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17246 Roo.bootstrap.version == 3 ? caret : '',
17249 cls: 'combobox-clear',
17263 combobox.cls += ' roo-select2-container-multi';
17266 var align = this.labelAlign || this.parentLabelAlign();
17268 if (align ==='left' && this.fieldLabel.length) {
17273 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17274 tooltip : 'This field is required'
17278 cls : 'control-label col-form-label',
17279 html : this.fieldLabel
17283 cls : 'roo-combobox-wrap ',
17290 var labelCfg = cfg.cn[1];
17291 var contentCfg = cfg.cn[2];
17294 if(this.indicatorpos == 'right'){
17299 cls : 'control-label col-form-label',
17303 html : this.fieldLabel
17307 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17308 tooltip : 'This field is required'
17313 cls : "roo-combobox-wrap ",
17321 labelCfg = cfg.cn[0];
17322 contentCfg = cfg.cn[1];
17327 if(this.labelWidth > 12){
17328 labelCfg.style = "width: " + this.labelWidth + 'px';
17331 if(this.labelWidth < 13 && this.labelmd == 0){
17332 this.labelmd = this.labelWidth;
17335 if(this.labellg > 0){
17336 labelCfg.cls += ' col-lg-' + this.labellg;
17337 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17340 if(this.labelmd > 0){
17341 labelCfg.cls += ' col-md-' + this.labelmd;
17342 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17345 if(this.labelsm > 0){
17346 labelCfg.cls += ' col-sm-' + this.labelsm;
17347 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17350 if(this.labelxs > 0){
17351 labelCfg.cls += ' col-xs-' + this.labelxs;
17352 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17356 } else if ( this.fieldLabel.length) {
17360 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17361 tooltip : 'This field is required'
17365 cls : 'control-label',
17366 html : this.fieldLabel
17377 if(this.indicatorpos == 'right'){
17381 cls : 'control-label',
17382 html : this.fieldLabel,
17386 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17387 tooltip : 'This field is required'
17404 var settings = this;
17406 ['xs','sm','md','lg'].map(function(size){
17407 if (settings[size]) {
17408 cfg.cls += ' col-' + size + '-' + settings[size];
17415 initTouchView : function()
17417 this.renderTouchView();
17419 this.touchViewEl.on('scroll', function(){
17420 this.el.dom.scrollTop = 0;
17423 this.originalValue = this.getValue();
17425 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17427 this.inputEl().on("click", this.showTouchView, this);
17428 if (this.triggerEl) {
17429 this.triggerEl.on("click", this.showTouchView, this);
17433 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17434 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17436 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17438 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17439 this.store.on('load', this.onTouchViewLoad, this);
17440 this.store.on('loadexception', this.onTouchViewLoadException, this);
17442 if(this.hiddenName){
17444 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17446 this.hiddenField.dom.value =
17447 this.hiddenValue !== undefined ? this.hiddenValue :
17448 this.value !== undefined ? this.value : '';
17450 this.el.dom.removeAttribute('name');
17451 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17455 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17456 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17459 if(this.removable && !this.multiple){
17460 var close = this.closeTriggerEl();
17462 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17463 close.on('click', this.removeBtnClick, this, close);
17467 * fix the bug in Safari iOS8
17469 this.inputEl().on("focus", function(e){
17470 document.activeElement.blur();
17473 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17480 renderTouchView : function()
17482 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17483 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17485 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17486 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17488 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17489 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17490 this.touchViewBodyEl.setStyle('overflow', 'auto');
17492 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17493 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17495 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17496 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17500 showTouchView : function()
17506 this.touchViewHeaderEl.hide();
17508 if(this.modalTitle.length){
17509 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17510 this.touchViewHeaderEl.show();
17513 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17514 this.touchViewEl.show();
17516 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17518 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17519 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17521 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17523 if(this.modalTitle.length){
17524 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17527 this.touchViewBodyEl.setHeight(bodyHeight);
17531 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17533 this.touchViewEl.addClass(['in','show']);
17536 if(this._touchViewMask){
17537 Roo.get(document.body).addClass("x-body-masked");
17538 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17539 this._touchViewMask.setStyle('z-index', 10000);
17540 this._touchViewMask.addClass('show');
17543 this.doTouchViewQuery();
17547 hideTouchView : function()
17549 this.touchViewEl.removeClass(['in','show']);
17553 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17555 this.touchViewEl.setStyle('display', 'none');
17558 if(this._touchViewMask){
17559 this._touchViewMask.removeClass('show');
17560 Roo.get(document.body).removeClass("x-body-masked");
17564 setTouchViewValue : function()
17571 Roo.each(this.tickItems, function(o){
17576 this.hideTouchView();
17579 doTouchViewQuery : function()
17588 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17592 if(!this.alwaysQuery || this.mode == 'local'){
17593 this.onTouchViewLoad();
17600 onTouchViewBeforeLoad : function(combo,opts)
17606 onTouchViewLoad : function()
17608 if(this.store.getCount() < 1){
17609 this.onTouchViewEmptyResults();
17613 this.clearTouchView();
17615 var rawValue = this.getRawValue();
17617 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17619 this.tickItems = [];
17621 this.store.data.each(function(d, rowIndex){
17622 var row = this.touchViewListGroup.createChild(template);
17624 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17625 row.addClass(d.data.cls);
17628 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17631 html : d.data[this.displayField]
17634 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17635 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17638 row.removeClass('selected');
17639 if(!this.multiple && this.valueField &&
17640 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17643 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17644 row.addClass('selected');
17647 if(this.multiple && this.valueField &&
17648 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17652 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17653 this.tickItems.push(d.data);
17656 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17660 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17662 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17664 if(this.modalTitle.length){
17665 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17668 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17670 if(this.mobile_restrict_height && listHeight < bodyHeight){
17671 this.touchViewBodyEl.setHeight(listHeight);
17676 if(firstChecked && listHeight > bodyHeight){
17677 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17682 onTouchViewLoadException : function()
17684 this.hideTouchView();
17687 onTouchViewEmptyResults : function()
17689 this.clearTouchView();
17691 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17693 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17697 clearTouchView : function()
17699 this.touchViewListGroup.dom.innerHTML = '';
17702 onTouchViewClick : function(e, el, o)
17704 e.preventDefault();
17707 var rowIndex = o.rowIndex;
17709 var r = this.store.getAt(rowIndex);
17711 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17713 if(!this.multiple){
17714 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17715 c.dom.removeAttribute('checked');
17718 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17720 this.setFromData(r.data);
17722 var close = this.closeTriggerEl();
17728 this.hideTouchView();
17730 this.fireEvent('select', this, r, rowIndex);
17735 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17736 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17737 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17741 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17742 this.addItem(r.data);
17743 this.tickItems.push(r.data);
17747 getAutoCreateNativeIOS : function()
17750 cls: 'form-group' //input-group,
17755 cls : 'roo-ios-select'
17759 combobox.name = this.name;
17762 if (this.disabled) {
17763 combobox.disabled = true;
17766 var settings = this;
17768 ['xs','sm','md','lg'].map(function(size){
17769 if (settings[size]) {
17770 cfg.cls += ' col-' + size + '-' + settings[size];
17780 initIOSView : function()
17782 this.store.on('load', this.onIOSViewLoad, this);
17787 onIOSViewLoad : function()
17789 if(this.store.getCount() < 1){
17793 this.clearIOSView();
17795 if(this.allowBlank) {
17797 var default_text = '-- SELECT --';
17799 if(this.placeholder.length){
17800 default_text = this.placeholder;
17803 if(this.emptyTitle.length){
17804 default_text += ' - ' + this.emptyTitle + ' -';
17807 var opt = this.inputEl().createChild({
17810 html : default_text
17814 o[this.valueField] = 0;
17815 o[this.displayField] = default_text;
17817 this.ios_options.push({
17824 this.store.data.each(function(d, rowIndex){
17828 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17829 html = d.data[this.displayField];
17834 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17835 value = d.data[this.valueField];
17844 if(this.value == d.data[this.valueField]){
17845 option['selected'] = true;
17848 var opt = this.inputEl().createChild(option);
17850 this.ios_options.push({
17857 this.inputEl().on('change', function(){
17858 this.fireEvent('select', this);
17863 clearIOSView: function()
17865 this.inputEl().dom.innerHTML = '';
17867 this.ios_options = [];
17870 setIOSValue: function(v)
17874 if(!this.ios_options){
17878 Roo.each(this.ios_options, function(opts){
17880 opts.el.dom.removeAttribute('selected');
17882 if(opts.data[this.valueField] != v){
17886 opts.el.dom.setAttribute('selected', true);
17892 * @cfg {Boolean} grow
17896 * @cfg {Number} growMin
17900 * @cfg {Number} growMax
17909 Roo.apply(Roo.bootstrap.ComboBox, {
17913 cls: 'modal-header',
17935 cls: 'list-group-item',
17939 cls: 'roo-combobox-list-group-item-value'
17943 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17957 listItemCheckbox : {
17959 cls: 'list-group-item',
17963 cls: 'roo-combobox-list-group-item-value'
17967 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17983 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17988 cls: 'modal-footer',
17996 cls: 'col-xs-6 text-left',
17999 cls: 'btn btn-danger roo-touch-view-cancel',
18005 cls: 'col-xs-6 text-right',
18008 cls: 'btn btn-success roo-touch-view-ok',
18019 Roo.apply(Roo.bootstrap.ComboBox, {
18021 touchViewTemplate : {
18023 cls: 'modal fade roo-combobox-touch-view',
18027 cls: 'modal-dialog',
18028 style : 'position:fixed', // we have to fix position....
18032 cls: 'modal-content',
18034 Roo.bootstrap.ComboBox.header,
18035 Roo.bootstrap.ComboBox.body,
18036 Roo.bootstrap.ComboBox.footer
18045 * Ext JS Library 1.1.1
18046 * Copyright(c) 2006-2007, Ext JS, LLC.
18048 * Originally Released Under LGPL - original licence link has changed is not relivant.
18051 * <script type="text/javascript">
18056 * @extends Roo.util.Observable
18057 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18058 * This class also supports single and multi selection modes. <br>
18059 * Create a data model bound view:
18061 var store = new Roo.data.Store(...);
18063 var view = new Roo.View({
18065 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18067 singleSelect: true,
18068 selectedClass: "ydataview-selected",
18072 // listen for node click?
18073 view.on("click", function(vw, index, node, e){
18074 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18078 dataModel.load("foobar.xml");
18080 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18082 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18083 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18085 * Note: old style constructor is still suported (container, template, config)
18088 * Create a new View
18089 * @param {Object} config The config object
18092 Roo.View = function(config, depreciated_tpl, depreciated_config){
18094 this.parent = false;
18096 if (typeof(depreciated_tpl) == 'undefined') {
18097 // new way.. - universal constructor.
18098 Roo.apply(this, config);
18099 this.el = Roo.get(this.el);
18102 this.el = Roo.get(config);
18103 this.tpl = depreciated_tpl;
18104 Roo.apply(this, depreciated_config);
18106 this.wrapEl = this.el.wrap().wrap();
18107 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18110 if(typeof(this.tpl) == "string"){
18111 this.tpl = new Roo.Template(this.tpl);
18113 // support xtype ctors..
18114 this.tpl = new Roo.factory(this.tpl, Roo);
18118 this.tpl.compile();
18123 * @event beforeclick
18124 * Fires before a click is processed. Returns false to cancel the default action.
18125 * @param {Roo.View} this
18126 * @param {Number} index The index of the target node
18127 * @param {HTMLElement} node The target node
18128 * @param {Roo.EventObject} e The raw event object
18130 "beforeclick" : true,
18133 * Fires when a template node is clicked.
18134 * @param {Roo.View} this
18135 * @param {Number} index The index of the target node
18136 * @param {HTMLElement} node The target node
18137 * @param {Roo.EventObject} e The raw event object
18142 * Fires when a template node is double clicked.
18143 * @param {Roo.View} this
18144 * @param {Number} index The index of the target node
18145 * @param {HTMLElement} node The target node
18146 * @param {Roo.EventObject} e The raw event object
18150 * @event contextmenu
18151 * Fires when a template node is right clicked.
18152 * @param {Roo.View} this
18153 * @param {Number} index The index of the target node
18154 * @param {HTMLElement} node The target node
18155 * @param {Roo.EventObject} e The raw event object
18157 "contextmenu" : true,
18159 * @event selectionchange
18160 * Fires when the selected nodes change.
18161 * @param {Roo.View} this
18162 * @param {Array} selections Array of the selected nodes
18164 "selectionchange" : true,
18167 * @event beforeselect
18168 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18169 * @param {Roo.View} this
18170 * @param {HTMLElement} node The node to be selected
18171 * @param {Array} selections Array of currently selected nodes
18173 "beforeselect" : true,
18175 * @event preparedata
18176 * Fires on every row to render, to allow you to change the data.
18177 * @param {Roo.View} this
18178 * @param {Object} data to be rendered (change this)
18180 "preparedata" : true
18188 "click": this.onClick,
18189 "dblclick": this.onDblClick,
18190 "contextmenu": this.onContextMenu,
18194 this.selections = [];
18196 this.cmp = new Roo.CompositeElementLite([]);
18198 this.store = Roo.factory(this.store, Roo.data);
18199 this.setStore(this.store, true);
18202 if ( this.footer && this.footer.xtype) {
18204 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18206 this.footer.dataSource = this.store;
18207 this.footer.container = fctr;
18208 this.footer = Roo.factory(this.footer, Roo);
18209 fctr.insertFirst(this.el);
18211 // this is a bit insane - as the paging toolbar seems to detach the el..
18212 // dom.parentNode.parentNode.parentNode
18213 // they get detached?
18217 Roo.View.superclass.constructor.call(this);
18222 Roo.extend(Roo.View, Roo.util.Observable, {
18225 * @cfg {Roo.data.Store} store Data store to load data from.
18230 * @cfg {String|Roo.Element} el The container element.
18235 * @cfg {String|Roo.Template} tpl The template used by this View
18239 * @cfg {String} dataName the named area of the template to use as the data area
18240 * Works with domtemplates roo-name="name"
18244 * @cfg {String} selectedClass The css class to add to selected nodes
18246 selectedClass : "x-view-selected",
18248 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18253 * @cfg {String} text to display on mask (default Loading)
18257 * @cfg {Boolean} multiSelect Allow multiple selection
18259 multiSelect : false,
18261 * @cfg {Boolean} singleSelect Allow single selection
18263 singleSelect: false,
18266 * @cfg {Boolean} toggleSelect - selecting
18268 toggleSelect : false,
18271 * @cfg {Boolean} tickable - selecting
18276 * Returns the element this view is bound to.
18277 * @return {Roo.Element}
18279 getEl : function(){
18280 return this.wrapEl;
18286 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18288 refresh : function(){
18289 //Roo.log('refresh');
18292 // if we are using something like 'domtemplate', then
18293 // the what gets used is:
18294 // t.applySubtemplate(NAME, data, wrapping data..)
18295 // the outer template then get' applied with
18296 // the store 'extra data'
18297 // and the body get's added to the
18298 // roo-name="data" node?
18299 // <span class='roo-tpl-{name}'></span> ?????
18303 this.clearSelections();
18304 this.el.update("");
18306 var records = this.store.getRange();
18307 if(records.length < 1) {
18309 // is this valid?? = should it render a template??
18311 this.el.update(this.emptyText);
18315 if (this.dataName) {
18316 this.el.update(t.apply(this.store.meta)); //????
18317 el = this.el.child('.roo-tpl-' + this.dataName);
18320 for(var i = 0, len = records.length; i < len; i++){
18321 var data = this.prepareData(records[i].data, i, records[i]);
18322 this.fireEvent("preparedata", this, data, i, records[i]);
18324 var d = Roo.apply({}, data);
18327 Roo.apply(d, {'roo-id' : Roo.id()});
18331 Roo.each(this.parent.item, function(item){
18332 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18335 Roo.apply(d, {'roo-data-checked' : 'checked'});
18339 html[html.length] = Roo.util.Format.trim(
18341 t.applySubtemplate(this.dataName, d, this.store.meta) :
18348 el.update(html.join(""));
18349 this.nodes = el.dom.childNodes;
18350 this.updateIndexes(0);
18355 * Function to override to reformat the data that is sent to
18356 * the template for each node.
18357 * DEPRICATED - use the preparedata event handler.
18358 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18359 * a JSON object for an UpdateManager bound view).
18361 prepareData : function(data, index, record)
18363 this.fireEvent("preparedata", this, data, index, record);
18367 onUpdate : function(ds, record){
18368 // Roo.log('on update');
18369 this.clearSelections();
18370 var index = this.store.indexOf(record);
18371 var n = this.nodes[index];
18372 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18373 n.parentNode.removeChild(n);
18374 this.updateIndexes(index, index);
18380 onAdd : function(ds, records, index)
18382 //Roo.log(['on Add', ds, records, index] );
18383 this.clearSelections();
18384 if(this.nodes.length == 0){
18388 var n = this.nodes[index];
18389 for(var i = 0, len = records.length; i < len; i++){
18390 var d = this.prepareData(records[i].data, i, records[i]);
18392 this.tpl.insertBefore(n, d);
18395 this.tpl.append(this.el, d);
18398 this.updateIndexes(index);
18401 onRemove : function(ds, record, index){
18402 // Roo.log('onRemove');
18403 this.clearSelections();
18404 var el = this.dataName ?
18405 this.el.child('.roo-tpl-' + this.dataName) :
18408 el.dom.removeChild(this.nodes[index]);
18409 this.updateIndexes(index);
18413 * Refresh an individual node.
18414 * @param {Number} index
18416 refreshNode : function(index){
18417 this.onUpdate(this.store, this.store.getAt(index));
18420 updateIndexes : function(startIndex, endIndex){
18421 var ns = this.nodes;
18422 startIndex = startIndex || 0;
18423 endIndex = endIndex || ns.length - 1;
18424 for(var i = startIndex; i <= endIndex; i++){
18425 ns[i].nodeIndex = i;
18430 * Changes the data store this view uses and refresh the view.
18431 * @param {Store} store
18433 setStore : function(store, initial){
18434 if(!initial && this.store){
18435 this.store.un("datachanged", this.refresh);
18436 this.store.un("add", this.onAdd);
18437 this.store.un("remove", this.onRemove);
18438 this.store.un("update", this.onUpdate);
18439 this.store.un("clear", this.refresh);
18440 this.store.un("beforeload", this.onBeforeLoad);
18441 this.store.un("load", this.onLoad);
18442 this.store.un("loadexception", this.onLoad);
18446 store.on("datachanged", this.refresh, this);
18447 store.on("add", this.onAdd, this);
18448 store.on("remove", this.onRemove, this);
18449 store.on("update", this.onUpdate, this);
18450 store.on("clear", this.refresh, this);
18451 store.on("beforeload", this.onBeforeLoad, this);
18452 store.on("load", this.onLoad, this);
18453 store.on("loadexception", this.onLoad, this);
18461 * onbeforeLoad - masks the loading area.
18464 onBeforeLoad : function(store,opts)
18466 //Roo.log('onBeforeLoad');
18468 this.el.update("");
18470 this.el.mask(this.mask ? this.mask : "Loading" );
18472 onLoad : function ()
18479 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18480 * @param {HTMLElement} node
18481 * @return {HTMLElement} The template node
18483 findItemFromChild : function(node){
18484 var el = this.dataName ?
18485 this.el.child('.roo-tpl-' + this.dataName,true) :
18488 if(!node || node.parentNode == el){
18491 var p = node.parentNode;
18492 while(p && p != el){
18493 if(p.parentNode == el){
18502 onClick : function(e){
18503 var item = this.findItemFromChild(e.getTarget());
18505 var index = this.indexOf(item);
18506 if(this.onItemClick(item, index, e) !== false){
18507 this.fireEvent("click", this, index, item, e);
18510 this.clearSelections();
18515 onContextMenu : function(e){
18516 var item = this.findItemFromChild(e.getTarget());
18518 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18523 onDblClick : function(e){
18524 var item = this.findItemFromChild(e.getTarget());
18526 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18530 onItemClick : function(item, index, e)
18532 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18535 if (this.toggleSelect) {
18536 var m = this.isSelected(item) ? 'unselect' : 'select';
18539 _t[m](item, true, false);
18542 if(this.multiSelect || this.singleSelect){
18543 if(this.multiSelect && e.shiftKey && this.lastSelection){
18544 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18546 this.select(item, this.multiSelect && e.ctrlKey);
18547 this.lastSelection = item;
18550 if(!this.tickable){
18551 e.preventDefault();
18559 * Get the number of selected nodes.
18562 getSelectionCount : function(){
18563 return this.selections.length;
18567 * Get the currently selected nodes.
18568 * @return {Array} An array of HTMLElements
18570 getSelectedNodes : function(){
18571 return this.selections;
18575 * Get the indexes of the selected nodes.
18578 getSelectedIndexes : function(){
18579 var indexes = [], s = this.selections;
18580 for(var i = 0, len = s.length; i < len; i++){
18581 indexes.push(s[i].nodeIndex);
18587 * Clear all selections
18588 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18590 clearSelections : function(suppressEvent){
18591 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18592 this.cmp.elements = this.selections;
18593 this.cmp.removeClass(this.selectedClass);
18594 this.selections = [];
18595 if(!suppressEvent){
18596 this.fireEvent("selectionchange", this, this.selections);
18602 * Returns true if the passed node is selected
18603 * @param {HTMLElement/Number} node The node or node index
18604 * @return {Boolean}
18606 isSelected : function(node){
18607 var s = this.selections;
18611 node = this.getNode(node);
18612 return s.indexOf(node) !== -1;
18617 * @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
18618 * @param {Boolean} keepExisting (optional) true to keep existing selections
18619 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18621 select : function(nodeInfo, keepExisting, suppressEvent){
18622 if(nodeInfo instanceof Array){
18624 this.clearSelections(true);
18626 for(var i = 0, len = nodeInfo.length; i < len; i++){
18627 this.select(nodeInfo[i], true, true);
18631 var node = this.getNode(nodeInfo);
18632 if(!node || this.isSelected(node)){
18633 return; // already selected.
18636 this.clearSelections(true);
18639 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18640 Roo.fly(node).addClass(this.selectedClass);
18641 this.selections.push(node);
18642 if(!suppressEvent){
18643 this.fireEvent("selectionchange", this, this.selections);
18651 * @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
18652 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18653 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18655 unselect : function(nodeInfo, keepExisting, suppressEvent)
18657 if(nodeInfo instanceof Array){
18658 Roo.each(this.selections, function(s) {
18659 this.unselect(s, nodeInfo);
18663 var node = this.getNode(nodeInfo);
18664 if(!node || !this.isSelected(node)){
18665 //Roo.log("not selected");
18666 return; // not selected.
18670 Roo.each(this.selections, function(s) {
18672 Roo.fly(node).removeClass(this.selectedClass);
18679 this.selections= ns;
18680 this.fireEvent("selectionchange", this, this.selections);
18684 * Gets a template node.
18685 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18686 * @return {HTMLElement} The node or null if it wasn't found
18688 getNode : function(nodeInfo){
18689 if(typeof nodeInfo == "string"){
18690 return document.getElementById(nodeInfo);
18691 }else if(typeof nodeInfo == "number"){
18692 return this.nodes[nodeInfo];
18698 * Gets a range template nodes.
18699 * @param {Number} startIndex
18700 * @param {Number} endIndex
18701 * @return {Array} An array of nodes
18703 getNodes : function(start, end){
18704 var ns = this.nodes;
18705 start = start || 0;
18706 end = typeof end == "undefined" ? ns.length - 1 : end;
18709 for(var i = start; i <= end; i++){
18713 for(var i = start; i >= end; i--){
18721 * Finds the index of the passed node
18722 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18723 * @return {Number} The index of the node or -1
18725 indexOf : function(node){
18726 node = this.getNode(node);
18727 if(typeof node.nodeIndex == "number"){
18728 return node.nodeIndex;
18730 var ns = this.nodes;
18731 for(var i = 0, len = ns.length; i < len; i++){
18742 * based on jquery fullcalendar
18746 Roo.bootstrap = Roo.bootstrap || {};
18748 * @class Roo.bootstrap.Calendar
18749 * @extends Roo.bootstrap.Component
18750 * Bootstrap Calendar class
18751 * @cfg {Boolean} loadMask (true|false) default false
18752 * @cfg {Object} header generate the user specific header of the calendar, default false
18755 * Create a new Container
18756 * @param {Object} config The config object
18761 Roo.bootstrap.Calendar = function(config){
18762 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18766 * Fires when a date is selected
18767 * @param {DatePicker} this
18768 * @param {Date} date The selected date
18772 * @event monthchange
18773 * Fires when the displayed month changes
18774 * @param {DatePicker} this
18775 * @param {Date} date The selected month
18777 'monthchange': true,
18779 * @event evententer
18780 * Fires when mouse over an event
18781 * @param {Calendar} this
18782 * @param {event} Event
18784 'evententer': true,
18786 * @event eventleave
18787 * Fires when the mouse leaves an
18788 * @param {Calendar} this
18791 'eventleave': true,
18793 * @event eventclick
18794 * Fires when the mouse click an
18795 * @param {Calendar} this
18804 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18807 * @cfg {Number} startDay
18808 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18816 getAutoCreate : function(){
18819 var fc_button = function(name, corner, style, content ) {
18820 return Roo.apply({},{
18822 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18824 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18827 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18838 style : 'width:100%',
18845 cls : 'fc-header-left',
18847 fc_button('prev', 'left', 'arrow', '‹' ),
18848 fc_button('next', 'right', 'arrow', '›' ),
18849 { tag: 'span', cls: 'fc-header-space' },
18850 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18858 cls : 'fc-header-center',
18862 cls: 'fc-header-title',
18865 html : 'month / year'
18873 cls : 'fc-header-right',
18875 /* fc_button('month', 'left', '', 'month' ),
18876 fc_button('week', '', '', 'week' ),
18877 fc_button('day', 'right', '', 'day' )
18889 header = this.header;
18892 var cal_heads = function() {
18894 // fixme - handle this.
18896 for (var i =0; i < Date.dayNames.length; i++) {
18897 var d = Date.dayNames[i];
18900 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18901 html : d.substring(0,3)
18905 ret[0].cls += ' fc-first';
18906 ret[6].cls += ' fc-last';
18909 var cal_cell = function(n) {
18912 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18917 cls: 'fc-day-number',
18921 cls: 'fc-day-content',
18925 style: 'position: relative;' // height: 17px;
18937 var cal_rows = function() {
18940 for (var r = 0; r < 6; r++) {
18947 for (var i =0; i < Date.dayNames.length; i++) {
18948 var d = Date.dayNames[i];
18949 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18952 row.cn[0].cls+=' fc-first';
18953 row.cn[0].cn[0].style = 'min-height:90px';
18954 row.cn[6].cls+=' fc-last';
18958 ret[0].cls += ' fc-first';
18959 ret[4].cls += ' fc-prev-last';
18960 ret[5].cls += ' fc-last';
18967 cls: 'fc-border-separate',
18968 style : 'width:100%',
18976 cls : 'fc-first fc-last',
18994 cls : 'fc-content',
18995 style : "position: relative;",
18998 cls : 'fc-view fc-view-month fc-grid',
18999 style : 'position: relative',
19000 unselectable : 'on',
19003 cls : 'fc-event-container',
19004 style : 'position:absolute;z-index:8;top:0;left:0;'
19022 initEvents : function()
19025 throw "can not find store for calendar";
19031 style: "text-align:center",
19035 style: "background-color:white;width:50%;margin:250 auto",
19039 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19050 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19052 var size = this.el.select('.fc-content', true).first().getSize();
19053 this.maskEl.setSize(size.width, size.height);
19054 this.maskEl.enableDisplayMode("block");
19055 if(!this.loadMask){
19056 this.maskEl.hide();
19059 this.store = Roo.factory(this.store, Roo.data);
19060 this.store.on('load', this.onLoad, this);
19061 this.store.on('beforeload', this.onBeforeLoad, this);
19065 this.cells = this.el.select('.fc-day',true);
19066 //Roo.log(this.cells);
19067 this.textNodes = this.el.query('.fc-day-number');
19068 this.cells.addClassOnOver('fc-state-hover');
19070 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19071 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19072 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19073 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19075 this.on('monthchange', this.onMonthChange, this);
19077 this.update(new Date().clearTime());
19080 resize : function() {
19081 var sz = this.el.getSize();
19083 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19084 this.el.select('.fc-day-content div',true).setHeight(34);
19089 showPrevMonth : function(e){
19090 this.update(this.activeDate.add("mo", -1));
19092 showToday : function(e){
19093 this.update(new Date().clearTime());
19096 showNextMonth : function(e){
19097 this.update(this.activeDate.add("mo", 1));
19101 showPrevYear : function(){
19102 this.update(this.activeDate.add("y", -1));
19106 showNextYear : function(){
19107 this.update(this.activeDate.add("y", 1));
19112 update : function(date)
19114 var vd = this.activeDate;
19115 this.activeDate = date;
19116 // if(vd && this.el){
19117 // var t = date.getTime();
19118 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19119 // Roo.log('using add remove');
19121 // this.fireEvent('monthchange', this, date);
19123 // this.cells.removeClass("fc-state-highlight");
19124 // this.cells.each(function(c){
19125 // if(c.dateValue == t){
19126 // c.addClass("fc-state-highlight");
19127 // setTimeout(function(){
19128 // try{c.dom.firstChild.focus();}catch(e){}
19138 var days = date.getDaysInMonth();
19140 var firstOfMonth = date.getFirstDateOfMonth();
19141 var startingPos = firstOfMonth.getDay()-this.startDay;
19143 if(startingPos < this.startDay){
19147 var pm = date.add(Date.MONTH, -1);
19148 var prevStart = pm.getDaysInMonth()-startingPos;
19150 this.cells = this.el.select('.fc-day',true);
19151 this.textNodes = this.el.query('.fc-day-number');
19152 this.cells.addClassOnOver('fc-state-hover');
19154 var cells = this.cells.elements;
19155 var textEls = this.textNodes;
19157 Roo.each(cells, function(cell){
19158 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19161 days += startingPos;
19163 // convert everything to numbers so it's fast
19164 var day = 86400000;
19165 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19168 //Roo.log(prevStart);
19170 var today = new Date().clearTime().getTime();
19171 var sel = date.clearTime().getTime();
19172 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19173 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19174 var ddMatch = this.disabledDatesRE;
19175 var ddText = this.disabledDatesText;
19176 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19177 var ddaysText = this.disabledDaysText;
19178 var format = this.format;
19180 var setCellClass = function(cal, cell){
19184 //Roo.log('set Cell Class');
19186 var t = d.getTime();
19190 cell.dateValue = t;
19192 cell.className += " fc-today";
19193 cell.className += " fc-state-highlight";
19194 cell.title = cal.todayText;
19197 // disable highlight in other month..
19198 //cell.className += " fc-state-highlight";
19203 cell.className = " fc-state-disabled";
19204 cell.title = cal.minText;
19208 cell.className = " fc-state-disabled";
19209 cell.title = cal.maxText;
19213 if(ddays.indexOf(d.getDay()) != -1){
19214 cell.title = ddaysText;
19215 cell.className = " fc-state-disabled";
19218 if(ddMatch && format){
19219 var fvalue = d.dateFormat(format);
19220 if(ddMatch.test(fvalue)){
19221 cell.title = ddText.replace("%0", fvalue);
19222 cell.className = " fc-state-disabled";
19226 if (!cell.initialClassName) {
19227 cell.initialClassName = cell.dom.className;
19230 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19235 for(; i < startingPos; i++) {
19236 textEls[i].innerHTML = (++prevStart);
19237 d.setDate(d.getDate()+1);
19239 cells[i].className = "fc-past fc-other-month";
19240 setCellClass(this, cells[i]);
19245 for(; i < days; i++){
19246 intDay = i - startingPos + 1;
19247 textEls[i].innerHTML = (intDay);
19248 d.setDate(d.getDate()+1);
19250 cells[i].className = ''; // "x-date-active";
19251 setCellClass(this, cells[i]);
19255 for(; i < 42; i++) {
19256 textEls[i].innerHTML = (++extraDays);
19257 d.setDate(d.getDate()+1);
19259 cells[i].className = "fc-future fc-other-month";
19260 setCellClass(this, cells[i]);
19263 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19265 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19267 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19268 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19270 if(totalRows != 6){
19271 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19272 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19275 this.fireEvent('monthchange', this, date);
19279 if(!this.internalRender){
19280 var main = this.el.dom.firstChild;
19281 var w = main.offsetWidth;
19282 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19283 Roo.fly(main).setWidth(w);
19284 this.internalRender = true;
19285 // opera does not respect the auto grow header center column
19286 // then, after it gets a width opera refuses to recalculate
19287 // without a second pass
19288 if(Roo.isOpera && !this.secondPass){
19289 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19290 this.secondPass = true;
19291 this.update.defer(10, this, [date]);
19298 findCell : function(dt) {
19299 dt = dt.clearTime().getTime();
19301 this.cells.each(function(c){
19302 //Roo.log("check " +c.dateValue + '?=' + dt);
19303 if(c.dateValue == dt){
19313 findCells : function(ev) {
19314 var s = ev.start.clone().clearTime().getTime();
19316 var e= ev.end.clone().clearTime().getTime();
19319 this.cells.each(function(c){
19320 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19322 if(c.dateValue > e){
19325 if(c.dateValue < s){
19334 // findBestRow: function(cells)
19338 // for (var i =0 ; i < cells.length;i++) {
19339 // ret = Math.max(cells[i].rows || 0,ret);
19346 addItem : function(ev)
19348 // look for vertical location slot in
19349 var cells = this.findCells(ev);
19351 // ev.row = this.findBestRow(cells);
19353 // work out the location.
19357 for(var i =0; i < cells.length; i++) {
19359 cells[i].row = cells[0].row;
19362 cells[i].row = cells[i].row + 1;
19372 if (crow.start.getY() == cells[i].getY()) {
19374 crow.end = cells[i];
19391 cells[0].events.push(ev);
19393 this.calevents.push(ev);
19396 clearEvents: function() {
19398 if(!this.calevents){
19402 Roo.each(this.cells.elements, function(c){
19408 Roo.each(this.calevents, function(e) {
19409 Roo.each(e.els, function(el) {
19410 el.un('mouseenter' ,this.onEventEnter, this);
19411 el.un('mouseleave' ,this.onEventLeave, this);
19416 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19422 renderEvents: function()
19426 this.cells.each(function(c) {
19435 if(c.row != c.events.length){
19436 r = 4 - (4 - (c.row - c.events.length));
19439 c.events = ev.slice(0, r);
19440 c.more = ev.slice(r);
19442 if(c.more.length && c.more.length == 1){
19443 c.events.push(c.more.pop());
19446 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19450 this.cells.each(function(c) {
19452 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19455 for (var e = 0; e < c.events.length; e++){
19456 var ev = c.events[e];
19457 var rows = ev.rows;
19459 for(var i = 0; i < rows.length; i++) {
19461 // how many rows should it span..
19464 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19465 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19467 unselectable : "on",
19470 cls: 'fc-event-inner',
19474 // cls: 'fc-event-time',
19475 // html : cells.length > 1 ? '' : ev.time
19479 cls: 'fc-event-title',
19480 html : String.format('{0}', ev.title)
19487 cls: 'ui-resizable-handle ui-resizable-e',
19488 html : '  '
19495 cfg.cls += ' fc-event-start';
19497 if ((i+1) == rows.length) {
19498 cfg.cls += ' fc-event-end';
19501 var ctr = _this.el.select('.fc-event-container',true).first();
19502 var cg = ctr.createChild(cfg);
19504 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19505 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19507 var r = (c.more.length) ? 1 : 0;
19508 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19509 cg.setWidth(ebox.right - sbox.x -2);
19511 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19512 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19513 cg.on('click', _this.onEventClick, _this, ev);
19524 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19525 style : 'position: absolute',
19526 unselectable : "on",
19529 cls: 'fc-event-inner',
19533 cls: 'fc-event-title',
19541 cls: 'ui-resizable-handle ui-resizable-e',
19542 html : '  '
19548 var ctr = _this.el.select('.fc-event-container',true).first();
19549 var cg = ctr.createChild(cfg);
19551 var sbox = c.select('.fc-day-content',true).first().getBox();
19552 var ebox = c.select('.fc-day-content',true).first().getBox();
19554 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19555 cg.setWidth(ebox.right - sbox.x -2);
19557 cg.on('click', _this.onMoreEventClick, _this, c.more);
19567 onEventEnter: function (e, el,event,d) {
19568 this.fireEvent('evententer', this, el, event);
19571 onEventLeave: function (e, el,event,d) {
19572 this.fireEvent('eventleave', this, el, event);
19575 onEventClick: function (e, el,event,d) {
19576 this.fireEvent('eventclick', this, el, event);
19579 onMonthChange: function () {
19583 onMoreEventClick: function(e, el, more)
19587 this.calpopover.placement = 'right';
19588 this.calpopover.setTitle('More');
19590 this.calpopover.setContent('');
19592 var ctr = this.calpopover.el.select('.popover-content', true).first();
19594 Roo.each(more, function(m){
19596 cls : 'fc-event-hori fc-event-draggable',
19599 var cg = ctr.createChild(cfg);
19601 cg.on('click', _this.onEventClick, _this, m);
19604 this.calpopover.show(el);
19609 onLoad: function ()
19611 this.calevents = [];
19614 if(this.store.getCount() > 0){
19615 this.store.data.each(function(d){
19618 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19619 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19620 time : d.data.start_time,
19621 title : d.data.title,
19622 description : d.data.description,
19623 venue : d.data.venue
19628 this.renderEvents();
19630 if(this.calevents.length && this.loadMask){
19631 this.maskEl.hide();
19635 onBeforeLoad: function()
19637 this.clearEvents();
19639 this.maskEl.show();
19653 * @class Roo.bootstrap.Popover
19654 * @extends Roo.bootstrap.Component
19655 * Bootstrap Popover class
19656 * @cfg {String} html contents of the popover (or false to use children..)
19657 * @cfg {String} title of popover (or false to hide)
19658 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19659 * @cfg {String} trigger click || hover (or false to trigger manually)
19660 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19661 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19662 * - if false and it has a 'parent' then it will be automatically added to that element
19663 * - if string - Roo.get will be called
19664 * @cfg {Number} delay - delay before showing
19667 * Create a new Popover
19668 * @param {Object} config The config object
19671 Roo.bootstrap.Popover = function(config){
19672 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19678 * After the popover show
19680 * @param {Roo.bootstrap.Popover} this
19685 * After the popover hide
19687 * @param {Roo.bootstrap.Popover} this
19693 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19698 placement : 'right',
19699 trigger : 'hover', // hover
19705 can_build_overlaid : false,
19707 maskEl : false, // the mask element
19710 alignEl : false, // when show is called with an element - this get's stored.
19712 getChildContainer : function()
19714 return this.contentEl;
19717 getPopoverHeader : function()
19719 this.title = true; // flag not to hide it..
19720 this.headerEl.addClass('p-0');
19721 return this.headerEl
19725 getAutoCreate : function(){
19728 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19729 style: 'display:block',
19735 cls : 'popover-inner ',
19739 cls: 'popover-title popover-header',
19740 html : this.title === false ? '' : this.title
19743 cls : 'popover-content popover-body ' + (this.cls || ''),
19744 html : this.html || ''
19755 * @param {string} the title
19757 setTitle: function(str)
19761 this.headerEl.dom.innerHTML = str;
19766 * @param {string} the body content
19768 setContent: function(str)
19771 if (this.contentEl) {
19772 this.contentEl.dom.innerHTML = str;
19776 // as it get's added to the bottom of the page.
19777 onRender : function(ct, position)
19779 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19784 var cfg = Roo.apply({}, this.getAutoCreate());
19788 cfg.cls += ' ' + this.cls;
19791 cfg.style = this.style;
19793 //Roo.log("adding to ");
19794 this.el = Roo.get(document.body).createChild(cfg, position);
19795 // Roo.log(this.el);
19798 this.contentEl = this.el.select('.popover-content',true).first();
19799 this.headerEl = this.el.select('.popover-title',true).first();
19802 if(typeof(this.items) != 'undefined'){
19803 var items = this.items;
19806 for(var i =0;i < items.length;i++) {
19807 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19811 this.items = nitems;
19813 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19814 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19821 resizeMask : function()
19823 this.maskEl.setSize(
19824 Roo.lib.Dom.getViewWidth(true),
19825 Roo.lib.Dom.getViewHeight(true)
19829 initEvents : function()
19833 Roo.bootstrap.Popover.register(this);
19836 this.arrowEl = this.el.select('.arrow',true).first();
19837 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19838 this.el.enableDisplayMode('block');
19842 if (this.over === false && !this.parent()) {
19845 if (this.triggers === false) {
19850 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19851 var triggers = this.trigger ? this.trigger.split(' ') : [];
19852 Roo.each(triggers, function(trigger) {
19854 if (trigger == 'click') {
19855 on_el.on('click', this.toggle, this);
19856 } else if (trigger != 'manual') {
19857 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19858 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19860 on_el.on(eventIn ,this.enter, this);
19861 on_el.on(eventOut, this.leave, this);
19871 toggle : function () {
19872 this.hoverState == 'in' ? this.leave() : this.enter();
19875 enter : function () {
19877 clearTimeout(this.timeout);
19879 this.hoverState = 'in';
19881 if (!this.delay || !this.delay.show) {
19886 this.timeout = setTimeout(function () {
19887 if (_t.hoverState == 'in') {
19890 }, this.delay.show)
19893 leave : function() {
19894 clearTimeout(this.timeout);
19896 this.hoverState = 'out';
19898 if (!this.delay || !this.delay.hide) {
19903 this.timeout = setTimeout(function () {
19904 if (_t.hoverState == 'out') {
19907 }, this.delay.hide)
19911 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19912 * @param {string} (left|right|top|bottom) position
19914 show : function (on_el, placement)
19916 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
19917 on_el = on_el || false; // default to false
19920 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19921 on_el = this.parent().el;
19922 } else if (this.over) {
19923 Roo.get(this.over);
19928 this.alignEl = Roo.get( on_el );
19931 this.render(document.body);
19937 if (this.title === false) {
19938 this.headerEl.hide();
19943 this.el.dom.style.display = 'block';
19946 if (this.alignEl) {
19947 this.updatePosition(this.placement, true);
19950 // this is usually just done by the builder = to show the popoup in the middle of the scren.
19951 var es = this.el.getSize();
19952 var x = Roo.lib.Dom.getViewWidth()/2;
19953 var y = Roo.lib.Dom.getViewHeight()/2;
19954 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
19959 //var arrow = this.el.select('.arrow',true).first();
19960 //arrow.set(align[2],
19962 this.el.addClass('in');
19966 this.hoverState = 'in';
19969 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19970 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19971 this.maskEl.dom.style.display = 'block';
19972 this.maskEl.addClass('show');
19974 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19976 this.fireEvent('show', this);
19980 * fire this manually after loading a grid in the table for example
19981 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
19982 * @param {Boolean} try and move it if we cant get right position.
19984 updatePosition : function(placement, try_move)
19986 // allow for calling with no parameters
19987 placement = placement ? placement : this.placement;
19988 try_move = typeof(try_move) == 'undefined' ? true : try_move;
19990 this.el.removeClass([
19991 'fade','top','bottom', 'left', 'right','in',
19992 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19994 this.el.addClass(placement + ' bs-popover-' + placement);
19996 if (!this.alignEl ) {
20000 switch (placement) {
20002 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20003 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20004 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20005 //normal display... or moved up/down.
20006 this.el.setXY(offset);
20007 var xy = this.alignEl.getAnchorXY('tr', false);
20009 this.arrowEl.setXY(xy);
20012 // continue through...
20013 return this.updatePosition('left', false);
20017 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20018 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20019 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20020 //normal display... or moved up/down.
20021 this.el.setXY(offset);
20022 var xy = this.alignEl.getAnchorXY('tl', false);
20023 xy[0]-=10;xy[1]+=5; // << fix me
20024 this.arrowEl.setXY(xy);
20028 return this.updatePosition('right', false);
20031 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20032 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20033 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20034 //normal display... or moved up/down.
20035 this.el.setXY(offset);
20036 var xy = this.alignEl.getAnchorXY('t', false);
20037 xy[1]-=10; // << fix me
20038 this.arrowEl.setXY(xy);
20042 return this.updatePosition('bottom', false);
20045 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20046 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20047 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20048 //normal display... or moved up/down.
20049 this.el.setXY(offset);
20050 var xy = this.alignEl.getAnchorXY('b', false);
20051 xy[1]+=2; // << fix me
20052 this.arrowEl.setXY(xy);
20056 return this.updatePosition('top', false);
20067 this.el.setXY([0,0]);
20068 this.el.removeClass('in');
20070 this.hoverState = null;
20071 this.maskEl.hide(); // always..
20072 this.fireEvent('hide', this);
20078 Roo.apply(Roo.bootstrap.Popover, {
20081 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20082 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20083 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20084 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20089 clickHander : false,
20092 onMouseDown : function(e)
20094 if (!e.getTarget(".roo-popover")) {
20102 register : function(popup)
20104 if (!Roo.bootstrap.Popover.clickHandler) {
20105 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20107 // hide other popups.
20109 this.popups.push(popup);
20111 hideAll : function()
20113 this.popups.forEach(function(p) {
20121 * Card header - holder for the card header elements.
20126 * @class Roo.bootstrap.PopoverNav
20127 * @extends Roo.bootstrap.NavGroup
20128 * Bootstrap Popover header navigation class
20130 * Create a new Popover Header Navigation
20131 * @param {Object} config The config object
20134 Roo.bootstrap.PopoverNav = function(config){
20135 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20138 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20141 container_method : 'getPopoverHeader'
20159 * @class Roo.bootstrap.Progress
20160 * @extends Roo.bootstrap.Component
20161 * Bootstrap Progress class
20162 * @cfg {Boolean} striped striped of the progress bar
20163 * @cfg {Boolean} active animated of the progress bar
20167 * Create a new Progress
20168 * @param {Object} config The config object
20171 Roo.bootstrap.Progress = function(config){
20172 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20175 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20180 getAutoCreate : function(){
20188 cfg.cls += ' progress-striped';
20192 cfg.cls += ' active';
20211 * @class Roo.bootstrap.ProgressBar
20212 * @extends Roo.bootstrap.Component
20213 * Bootstrap ProgressBar class
20214 * @cfg {Number} aria_valuenow aria-value now
20215 * @cfg {Number} aria_valuemin aria-value min
20216 * @cfg {Number} aria_valuemax aria-value max
20217 * @cfg {String} label label for the progress bar
20218 * @cfg {String} panel (success | info | warning | danger )
20219 * @cfg {String} role role of the progress bar
20220 * @cfg {String} sr_only text
20224 * Create a new ProgressBar
20225 * @param {Object} config The config object
20228 Roo.bootstrap.ProgressBar = function(config){
20229 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20232 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20236 aria_valuemax : 100,
20242 getAutoCreate : function()
20247 cls: 'progress-bar',
20248 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20260 cfg.role = this.role;
20263 if(this.aria_valuenow){
20264 cfg['aria-valuenow'] = this.aria_valuenow;
20267 if(this.aria_valuemin){
20268 cfg['aria-valuemin'] = this.aria_valuemin;
20271 if(this.aria_valuemax){
20272 cfg['aria-valuemax'] = this.aria_valuemax;
20275 if(this.label && !this.sr_only){
20276 cfg.html = this.label;
20280 cfg.cls += ' progress-bar-' + this.panel;
20286 update : function(aria_valuenow)
20288 this.aria_valuenow = aria_valuenow;
20290 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20305 * @class Roo.bootstrap.TabGroup
20306 * @extends Roo.bootstrap.Column
20307 * Bootstrap Column class
20308 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20309 * @cfg {Boolean} carousel true to make the group behave like a carousel
20310 * @cfg {Boolean} bullets show bullets for the panels
20311 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20312 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20313 * @cfg {Boolean} showarrow (true|false) show arrow default true
20316 * Create a new TabGroup
20317 * @param {Object} config The config object
20320 Roo.bootstrap.TabGroup = function(config){
20321 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20323 this.navId = Roo.id();
20326 Roo.bootstrap.TabGroup.register(this);
20330 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20333 transition : false,
20338 slideOnTouch : false,
20341 getAutoCreate : function()
20343 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20345 cfg.cls += ' tab-content';
20347 if (this.carousel) {
20348 cfg.cls += ' carousel slide';
20351 cls : 'carousel-inner',
20355 if(this.bullets && !Roo.isTouch){
20358 cls : 'carousel-bullets',
20362 if(this.bullets_cls){
20363 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20370 cfg.cn[0].cn.push(bullets);
20373 if(this.showarrow){
20374 cfg.cn[0].cn.push({
20376 class : 'carousel-arrow',
20380 class : 'carousel-prev',
20384 class : 'fa fa-chevron-left'
20390 class : 'carousel-next',
20394 class : 'fa fa-chevron-right'
20407 initEvents: function()
20409 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20410 // this.el.on("touchstart", this.onTouchStart, this);
20413 if(this.autoslide){
20416 this.slideFn = window.setInterval(function() {
20417 _this.showPanelNext();
20421 if(this.showarrow){
20422 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20423 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20429 // onTouchStart : function(e, el, o)
20431 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20435 // this.showPanelNext();
20439 getChildContainer : function()
20441 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20445 * register a Navigation item
20446 * @param {Roo.bootstrap.NavItem} the navitem to add
20448 register : function(item)
20450 this.tabs.push( item);
20451 item.navId = this.navId; // not really needed..
20456 getActivePanel : function()
20459 Roo.each(this.tabs, function(t) {
20469 getPanelByName : function(n)
20472 Roo.each(this.tabs, function(t) {
20473 if (t.tabId == n) {
20481 indexOfPanel : function(p)
20484 Roo.each(this.tabs, function(t,i) {
20485 if (t.tabId == p.tabId) {
20494 * show a specific panel
20495 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20496 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20498 showPanel : function (pan)
20500 if(this.transition || typeof(pan) == 'undefined'){
20501 Roo.log("waiting for the transitionend");
20505 if (typeof(pan) == 'number') {
20506 pan = this.tabs[pan];
20509 if (typeof(pan) == 'string') {
20510 pan = this.getPanelByName(pan);
20513 var cur = this.getActivePanel();
20516 Roo.log('pan or acitve pan is undefined');
20520 if (pan.tabId == this.getActivePanel().tabId) {
20524 if (false === cur.fireEvent('beforedeactivate')) {
20528 if(this.bullets > 0 && !Roo.isTouch){
20529 this.setActiveBullet(this.indexOfPanel(pan));
20532 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20534 //class="carousel-item carousel-item-next carousel-item-left"
20536 this.transition = true;
20537 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20538 var lr = dir == 'next' ? 'left' : 'right';
20539 pan.el.addClass(dir); // or prev
20540 pan.el.addClass('carousel-item-' + dir); // or prev
20541 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20542 cur.el.addClass(lr); // or right
20543 pan.el.addClass(lr);
20544 cur.el.addClass('carousel-item-' +lr); // or right
20545 pan.el.addClass('carousel-item-' +lr);
20549 cur.el.on('transitionend', function() {
20550 Roo.log("trans end?");
20552 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20553 pan.setActive(true);
20555 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20556 cur.setActive(false);
20558 _this.transition = false;
20560 }, this, { single: true } );
20565 cur.setActive(false);
20566 pan.setActive(true);
20571 showPanelNext : function()
20573 var i = this.indexOfPanel(this.getActivePanel());
20575 if (i >= this.tabs.length - 1 && !this.autoslide) {
20579 if (i >= this.tabs.length - 1 && this.autoslide) {
20583 this.showPanel(this.tabs[i+1]);
20586 showPanelPrev : function()
20588 var i = this.indexOfPanel(this.getActivePanel());
20590 if (i < 1 && !this.autoslide) {
20594 if (i < 1 && this.autoslide) {
20595 i = this.tabs.length;
20598 this.showPanel(this.tabs[i-1]);
20602 addBullet: function()
20604 if(!this.bullets || Roo.isTouch){
20607 var ctr = this.el.select('.carousel-bullets',true).first();
20608 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20609 var bullet = ctr.createChild({
20610 cls : 'bullet bullet-' + i
20611 },ctr.dom.lastChild);
20616 bullet.on('click', (function(e, el, o, ii, t){
20618 e.preventDefault();
20620 this.showPanel(ii);
20622 if(this.autoslide && this.slideFn){
20623 clearInterval(this.slideFn);
20624 this.slideFn = window.setInterval(function() {
20625 _this.showPanelNext();
20629 }).createDelegate(this, [i, bullet], true));
20634 setActiveBullet : function(i)
20640 Roo.each(this.el.select('.bullet', true).elements, function(el){
20641 el.removeClass('selected');
20644 var bullet = this.el.select('.bullet-' + i, true).first();
20650 bullet.addClass('selected');
20661 Roo.apply(Roo.bootstrap.TabGroup, {
20665 * register a Navigation Group
20666 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20668 register : function(navgrp)
20670 this.groups[navgrp.navId] = navgrp;
20674 * fetch a Navigation Group based on the navigation ID
20675 * if one does not exist , it will get created.
20676 * @param {string} the navgroup to add
20677 * @returns {Roo.bootstrap.NavGroup} the navgroup
20679 get: function(navId) {
20680 if (typeof(this.groups[navId]) == 'undefined') {
20681 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20683 return this.groups[navId] ;
20698 * @class Roo.bootstrap.TabPanel
20699 * @extends Roo.bootstrap.Component
20700 * Bootstrap TabPanel class
20701 * @cfg {Boolean} active panel active
20702 * @cfg {String} html panel content
20703 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20704 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20705 * @cfg {String} href click to link..
20706 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20710 * Create a new TabPanel
20711 * @param {Object} config The config object
20714 Roo.bootstrap.TabPanel = function(config){
20715 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20719 * Fires when the active status changes
20720 * @param {Roo.bootstrap.TabPanel} this
20721 * @param {Boolean} state the new state
20726 * @event beforedeactivate
20727 * Fires before a tab is de-activated - can be used to do validation on a form.
20728 * @param {Roo.bootstrap.TabPanel} this
20729 * @return {Boolean} false if there is an error
20732 'beforedeactivate': true
20735 this.tabId = this.tabId || Roo.id();
20739 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20746 touchSlide : false,
20747 getAutoCreate : function(){
20752 // item is needed for carousel - not sure if it has any effect otherwise
20753 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20754 html: this.html || ''
20758 cfg.cls += ' active';
20762 cfg.tabId = this.tabId;
20770 initEvents: function()
20772 var p = this.parent();
20774 this.navId = this.navId || p.navId;
20776 if (typeof(this.navId) != 'undefined') {
20777 // not really needed.. but just in case.. parent should be a NavGroup.
20778 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20782 var i = tg.tabs.length - 1;
20784 if(this.active && tg.bullets > 0 && i < tg.bullets){
20785 tg.setActiveBullet(i);
20789 this.el.on('click', this.onClick, this);
20791 if(Roo.isTouch && this.touchSlide){
20792 this.el.on("touchstart", this.onTouchStart, this);
20793 this.el.on("touchmove", this.onTouchMove, this);
20794 this.el.on("touchend", this.onTouchEnd, this);
20799 onRender : function(ct, position)
20801 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20804 setActive : function(state)
20806 Roo.log("panel - set active " + this.tabId + "=" + state);
20808 this.active = state;
20810 this.el.removeClass('active');
20812 } else if (!this.el.hasClass('active')) {
20813 this.el.addClass('active');
20816 this.fireEvent('changed', this, state);
20819 onClick : function(e)
20821 e.preventDefault();
20823 if(!this.href.length){
20827 window.location.href = this.href;
20836 onTouchStart : function(e)
20838 this.swiping = false;
20840 this.startX = e.browserEvent.touches[0].clientX;
20841 this.startY = e.browserEvent.touches[0].clientY;
20844 onTouchMove : function(e)
20846 this.swiping = true;
20848 this.endX = e.browserEvent.touches[0].clientX;
20849 this.endY = e.browserEvent.touches[0].clientY;
20852 onTouchEnd : function(e)
20859 var tabGroup = this.parent();
20861 if(this.endX > this.startX){ // swiping right
20862 tabGroup.showPanelPrev();
20866 if(this.startX > this.endX){ // swiping left
20867 tabGroup.showPanelNext();
20886 * @class Roo.bootstrap.DateField
20887 * @extends Roo.bootstrap.Input
20888 * Bootstrap DateField class
20889 * @cfg {Number} weekStart default 0
20890 * @cfg {String} viewMode default empty, (months|years)
20891 * @cfg {String} minViewMode default empty, (months|years)
20892 * @cfg {Number} startDate default -Infinity
20893 * @cfg {Number} endDate default Infinity
20894 * @cfg {Boolean} todayHighlight default false
20895 * @cfg {Boolean} todayBtn default false
20896 * @cfg {Boolean} calendarWeeks default false
20897 * @cfg {Object} daysOfWeekDisabled default empty
20898 * @cfg {Boolean} singleMode default false (true | false)
20900 * @cfg {Boolean} keyboardNavigation default true
20901 * @cfg {String} language default en
20904 * Create a new DateField
20905 * @param {Object} config The config object
20908 Roo.bootstrap.DateField = function(config){
20909 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20913 * Fires when this field show.
20914 * @param {Roo.bootstrap.DateField} this
20915 * @param {Mixed} date The date value
20920 * Fires when this field hide.
20921 * @param {Roo.bootstrap.DateField} this
20922 * @param {Mixed} date The date value
20927 * Fires when select a date.
20928 * @param {Roo.bootstrap.DateField} this
20929 * @param {Mixed} date The date value
20933 * @event beforeselect
20934 * Fires when before select a date.
20935 * @param {Roo.bootstrap.DateField} this
20936 * @param {Mixed} date The date value
20938 beforeselect : true
20942 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20945 * @cfg {String} format
20946 * The default date format string which can be overriden for localization support. The format must be
20947 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20951 * @cfg {String} altFormats
20952 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20953 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20955 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20963 todayHighlight : false,
20969 keyboardNavigation: true,
20971 calendarWeeks: false,
20973 startDate: -Infinity,
20977 daysOfWeekDisabled: [],
20981 singleMode : false,
20983 UTCDate: function()
20985 return new Date(Date.UTC.apply(Date, arguments));
20988 UTCToday: function()
20990 var today = new Date();
20991 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20994 getDate: function() {
20995 var d = this.getUTCDate();
20996 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20999 getUTCDate: function() {
21003 setDate: function(d) {
21004 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21007 setUTCDate: function(d) {
21009 this.setValue(this.formatDate(this.date));
21012 onRender: function(ct, position)
21015 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21017 this.language = this.language || 'en';
21018 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21019 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21021 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21022 this.format = this.format || 'm/d/y';
21023 this.isInline = false;
21024 this.isInput = true;
21025 this.component = this.el.select('.add-on', true).first() || false;
21026 this.component = (this.component && this.component.length === 0) ? false : this.component;
21027 this.hasInput = this.component && this.inputEl().length;
21029 if (typeof(this.minViewMode === 'string')) {
21030 switch (this.minViewMode) {
21032 this.minViewMode = 1;
21035 this.minViewMode = 2;
21038 this.minViewMode = 0;
21043 if (typeof(this.viewMode === 'string')) {
21044 switch (this.viewMode) {
21057 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21059 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21061 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21063 this.picker().on('mousedown', this.onMousedown, this);
21064 this.picker().on('click', this.onClick, this);
21066 this.picker().addClass('datepicker-dropdown');
21068 this.startViewMode = this.viewMode;
21070 if(this.singleMode){
21071 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21072 v.setVisibilityMode(Roo.Element.DISPLAY);
21076 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21077 v.setStyle('width', '189px');
21081 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21082 if(!this.calendarWeeks){
21087 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21088 v.attr('colspan', function(i, val){
21089 return parseInt(val) + 1;
21094 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21096 this.setStartDate(this.startDate);
21097 this.setEndDate(this.endDate);
21099 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21106 if(this.isInline) {
21111 picker : function()
21113 return this.pickerEl;
21114 // return this.el.select('.datepicker', true).first();
21117 fillDow: function()
21119 var dowCnt = this.weekStart;
21128 if(this.calendarWeeks){
21136 while (dowCnt < this.weekStart + 7) {
21140 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21144 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21147 fillMonths: function()
21150 var months = this.picker().select('>.datepicker-months td', true).first();
21152 months.dom.innerHTML = '';
21158 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21161 months.createChild(month);
21168 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;
21170 if (this.date < this.startDate) {
21171 this.viewDate = new Date(this.startDate);
21172 } else if (this.date > this.endDate) {
21173 this.viewDate = new Date(this.endDate);
21175 this.viewDate = new Date(this.date);
21183 var d = new Date(this.viewDate),
21184 year = d.getUTCFullYear(),
21185 month = d.getUTCMonth(),
21186 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21187 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21188 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21189 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21190 currentDate = this.date && this.date.valueOf(),
21191 today = this.UTCToday();
21193 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21195 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21197 // this.picker.select('>tfoot th.today').
21198 // .text(dates[this.language].today)
21199 // .toggle(this.todayBtn !== false);
21201 this.updateNavArrows();
21204 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21206 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21208 prevMonth.setUTCDate(day);
21210 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21212 var nextMonth = new Date(prevMonth);
21214 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21216 nextMonth = nextMonth.valueOf();
21218 var fillMonths = false;
21220 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21222 while(prevMonth.valueOf() <= nextMonth) {
21225 if (prevMonth.getUTCDay() === this.weekStart) {
21227 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21235 if(this.calendarWeeks){
21236 // ISO 8601: First week contains first thursday.
21237 // ISO also states week starts on Monday, but we can be more abstract here.
21239 // Start of current week: based on weekstart/current date
21240 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21241 // Thursday of this week
21242 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21243 // First Thursday of year, year from thursday
21244 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21245 // Calendar week: ms between thursdays, div ms per day, div 7 days
21246 calWeek = (th - yth) / 864e5 / 7 + 1;
21248 fillMonths.cn.push({
21256 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21258 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21261 if (this.todayHighlight &&
21262 prevMonth.getUTCFullYear() == today.getFullYear() &&
21263 prevMonth.getUTCMonth() == today.getMonth() &&
21264 prevMonth.getUTCDate() == today.getDate()) {
21265 clsName += ' today';
21268 if (currentDate && prevMonth.valueOf() === currentDate) {
21269 clsName += ' active';
21272 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21273 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21274 clsName += ' disabled';
21277 fillMonths.cn.push({
21279 cls: 'day ' + clsName,
21280 html: prevMonth.getDate()
21283 prevMonth.setDate(prevMonth.getDate()+1);
21286 var currentYear = this.date && this.date.getUTCFullYear();
21287 var currentMonth = this.date && this.date.getUTCMonth();
21289 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21291 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21292 v.removeClass('active');
21294 if(currentYear === year && k === currentMonth){
21295 v.addClass('active');
21298 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21299 v.addClass('disabled');
21305 year = parseInt(year/10, 10) * 10;
21307 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21309 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21312 for (var i = -1; i < 11; i++) {
21313 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21315 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21323 showMode: function(dir)
21326 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21329 Roo.each(this.picker().select('>div',true).elements, function(v){
21330 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21333 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21338 if(this.isInline) {
21342 this.picker().removeClass(['bottom', 'top']);
21344 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21346 * place to the top of element!
21350 this.picker().addClass('top');
21351 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21356 this.picker().addClass('bottom');
21358 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21361 parseDate : function(value)
21363 if(!value || value instanceof Date){
21366 var v = Date.parseDate(value, this.format);
21367 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21368 v = Date.parseDate(value, 'Y-m-d');
21370 if(!v && this.altFormats){
21371 if(!this.altFormatsArray){
21372 this.altFormatsArray = this.altFormats.split("|");
21374 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21375 v = Date.parseDate(value, this.altFormatsArray[i]);
21381 formatDate : function(date, fmt)
21383 return (!date || !(date instanceof Date)) ?
21384 date : date.dateFormat(fmt || this.format);
21387 onFocus : function()
21389 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21393 onBlur : function()
21395 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21397 var d = this.inputEl().getValue();
21404 showPopup : function()
21406 this.picker().show();
21410 this.fireEvent('showpopup', this, this.date);
21413 hidePopup : function()
21415 if(this.isInline) {
21418 this.picker().hide();
21419 this.viewMode = this.startViewMode;
21422 this.fireEvent('hidepopup', this, this.date);
21426 onMousedown: function(e)
21428 e.stopPropagation();
21429 e.preventDefault();
21434 Roo.bootstrap.DateField.superclass.keyup.call(this);
21438 setValue: function(v)
21440 if(this.fireEvent('beforeselect', this, v) !== false){
21441 var d = new Date(this.parseDate(v) ).clearTime();
21443 if(isNaN(d.getTime())){
21444 this.date = this.viewDate = '';
21445 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21449 v = this.formatDate(d);
21451 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21453 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21457 this.fireEvent('select', this, this.date);
21461 getValue: function()
21463 return this.formatDate(this.date);
21466 fireKey: function(e)
21468 if (!this.picker().isVisible()){
21469 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21475 var dateChanged = false,
21477 newDate, newViewDate;
21482 e.preventDefault();
21486 if (!this.keyboardNavigation) {
21489 dir = e.keyCode == 37 ? -1 : 1;
21492 newDate = this.moveYear(this.date, dir);
21493 newViewDate = this.moveYear(this.viewDate, dir);
21494 } else if (e.shiftKey){
21495 newDate = this.moveMonth(this.date, dir);
21496 newViewDate = this.moveMonth(this.viewDate, dir);
21498 newDate = new Date(this.date);
21499 newDate.setUTCDate(this.date.getUTCDate() + dir);
21500 newViewDate = new Date(this.viewDate);
21501 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21503 if (this.dateWithinRange(newDate)){
21504 this.date = newDate;
21505 this.viewDate = newViewDate;
21506 this.setValue(this.formatDate(this.date));
21508 e.preventDefault();
21509 dateChanged = true;
21514 if (!this.keyboardNavigation) {
21517 dir = e.keyCode == 38 ? -1 : 1;
21519 newDate = this.moveYear(this.date, dir);
21520 newViewDate = this.moveYear(this.viewDate, dir);
21521 } else if (e.shiftKey){
21522 newDate = this.moveMonth(this.date, dir);
21523 newViewDate = this.moveMonth(this.viewDate, dir);
21525 newDate = new Date(this.date);
21526 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21527 newViewDate = new Date(this.viewDate);
21528 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21530 if (this.dateWithinRange(newDate)){
21531 this.date = newDate;
21532 this.viewDate = newViewDate;
21533 this.setValue(this.formatDate(this.date));
21535 e.preventDefault();
21536 dateChanged = true;
21540 this.setValue(this.formatDate(this.date));
21542 e.preventDefault();
21545 this.setValue(this.formatDate(this.date));
21559 onClick: function(e)
21561 e.stopPropagation();
21562 e.preventDefault();
21564 var target = e.getTarget();
21566 if(target.nodeName.toLowerCase() === 'i'){
21567 target = Roo.get(target).dom.parentNode;
21570 var nodeName = target.nodeName;
21571 var className = target.className;
21572 var html = target.innerHTML;
21573 //Roo.log(nodeName);
21575 switch(nodeName.toLowerCase()) {
21577 switch(className) {
21583 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21584 switch(this.viewMode){
21586 this.viewDate = this.moveMonth(this.viewDate, dir);
21590 this.viewDate = this.moveYear(this.viewDate, dir);
21596 var date = new Date();
21597 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21599 this.setValue(this.formatDate(this.date));
21606 if (className.indexOf('disabled') < 0) {
21607 this.viewDate.setUTCDate(1);
21608 if (className.indexOf('month') > -1) {
21609 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21611 var year = parseInt(html, 10) || 0;
21612 this.viewDate.setUTCFullYear(year);
21616 if(this.singleMode){
21617 this.setValue(this.formatDate(this.viewDate));
21628 //Roo.log(className);
21629 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21630 var day = parseInt(html, 10) || 1;
21631 var year = (this.viewDate || new Date()).getUTCFullYear(),
21632 month = (this.viewDate || new Date()).getUTCMonth();
21634 if (className.indexOf('old') > -1) {
21641 } else if (className.indexOf('new') > -1) {
21649 //Roo.log([year,month,day]);
21650 this.date = this.UTCDate(year, month, day,0,0,0,0);
21651 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21653 //Roo.log(this.formatDate(this.date));
21654 this.setValue(this.formatDate(this.date));
21661 setStartDate: function(startDate)
21663 this.startDate = startDate || -Infinity;
21664 if (this.startDate !== -Infinity) {
21665 this.startDate = this.parseDate(this.startDate);
21668 this.updateNavArrows();
21671 setEndDate: function(endDate)
21673 this.endDate = endDate || Infinity;
21674 if (this.endDate !== Infinity) {
21675 this.endDate = this.parseDate(this.endDate);
21678 this.updateNavArrows();
21681 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21683 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21684 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21685 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21687 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21688 return parseInt(d, 10);
21691 this.updateNavArrows();
21694 updateNavArrows: function()
21696 if(this.singleMode){
21700 var d = new Date(this.viewDate),
21701 year = d.getUTCFullYear(),
21702 month = d.getUTCMonth();
21704 Roo.each(this.picker().select('.prev', true).elements, function(v){
21706 switch (this.viewMode) {
21709 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21715 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21722 Roo.each(this.picker().select('.next', true).elements, function(v){
21724 switch (this.viewMode) {
21727 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21733 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21741 moveMonth: function(date, dir)
21746 var new_date = new Date(date.valueOf()),
21747 day = new_date.getUTCDate(),
21748 month = new_date.getUTCMonth(),
21749 mag = Math.abs(dir),
21751 dir = dir > 0 ? 1 : -1;
21754 // If going back one month, make sure month is not current month
21755 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21757 return new_date.getUTCMonth() == month;
21759 // If going forward one month, make sure month is as expected
21760 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21762 return new_date.getUTCMonth() != new_month;
21764 new_month = month + dir;
21765 new_date.setUTCMonth(new_month);
21766 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21767 if (new_month < 0 || new_month > 11) {
21768 new_month = (new_month + 12) % 12;
21771 // For magnitudes >1, move one month at a time...
21772 for (var i=0; i<mag; i++) {
21773 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21774 new_date = this.moveMonth(new_date, dir);
21776 // ...then reset the day, keeping it in the new month
21777 new_month = new_date.getUTCMonth();
21778 new_date.setUTCDate(day);
21780 return new_month != new_date.getUTCMonth();
21783 // Common date-resetting loop -- if date is beyond end of month, make it
21786 new_date.setUTCDate(--day);
21787 new_date.setUTCMonth(new_month);
21792 moveYear: function(date, dir)
21794 return this.moveMonth(date, dir*12);
21797 dateWithinRange: function(date)
21799 return date >= this.startDate && date <= this.endDate;
21805 this.picker().remove();
21808 validateValue : function(value)
21810 if(this.getVisibilityEl().hasClass('hidden')){
21814 if(value.length < 1) {
21815 if(this.allowBlank){
21821 if(value.length < this.minLength){
21824 if(value.length > this.maxLength){
21828 var vt = Roo.form.VTypes;
21829 if(!vt[this.vtype](value, this)){
21833 if(typeof this.validator == "function"){
21834 var msg = this.validator(value);
21840 if(this.regex && !this.regex.test(value)){
21844 if(typeof(this.parseDate(value)) == 'undefined'){
21848 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21852 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21862 this.date = this.viewDate = '';
21864 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21869 Roo.apply(Roo.bootstrap.DateField, {
21880 html: '<i class="fa fa-arrow-left"/>'
21890 html: '<i class="fa fa-arrow-right"/>'
21932 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21933 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21934 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21935 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21936 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21949 navFnc: 'FullYear',
21954 navFnc: 'FullYear',
21959 Roo.apply(Roo.bootstrap.DateField, {
21963 cls: 'datepicker dropdown-menu roo-dynamic shadow',
21967 cls: 'datepicker-days',
21971 cls: 'table-condensed',
21973 Roo.bootstrap.DateField.head,
21977 Roo.bootstrap.DateField.footer
21984 cls: 'datepicker-months',
21988 cls: 'table-condensed',
21990 Roo.bootstrap.DateField.head,
21991 Roo.bootstrap.DateField.content,
21992 Roo.bootstrap.DateField.footer
21999 cls: 'datepicker-years',
22003 cls: 'table-condensed',
22005 Roo.bootstrap.DateField.head,
22006 Roo.bootstrap.DateField.content,
22007 Roo.bootstrap.DateField.footer
22026 * @class Roo.bootstrap.TimeField
22027 * @extends Roo.bootstrap.Input
22028 * Bootstrap DateField class
22032 * Create a new TimeField
22033 * @param {Object} config The config object
22036 Roo.bootstrap.TimeField = function(config){
22037 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22041 * Fires when this field show.
22042 * @param {Roo.bootstrap.DateField} thisthis
22043 * @param {Mixed} date The date value
22048 * Fires when this field hide.
22049 * @param {Roo.bootstrap.DateField} this
22050 * @param {Mixed} date The date value
22055 * Fires when select a date.
22056 * @param {Roo.bootstrap.DateField} this
22057 * @param {Mixed} date The date value
22063 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22066 * @cfg {String} format
22067 * The default time format string which can be overriden for localization support. The format must be
22068 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22072 getAutoCreate : function()
22074 this.after = '<i class="fa far fa-clock"></i>';
22075 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22079 onRender: function(ct, position)
22082 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22084 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22086 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22088 this.pop = this.picker().select('>.datepicker-time',true).first();
22089 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22091 this.picker().on('mousedown', this.onMousedown, this);
22092 this.picker().on('click', this.onClick, this);
22094 this.picker().addClass('datepicker-dropdown');
22099 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22100 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22101 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22102 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22103 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22104 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22108 fireKey: function(e){
22109 if (!this.picker().isVisible()){
22110 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22116 e.preventDefault();
22124 this.onTogglePeriod();
22127 this.onIncrementMinutes();
22130 this.onDecrementMinutes();
22139 onClick: function(e) {
22140 e.stopPropagation();
22141 e.preventDefault();
22144 picker : function()
22146 return this.pickerEl;
22149 fillTime: function()
22151 var time = this.pop.select('tbody', true).first();
22153 time.dom.innerHTML = '';
22168 cls: 'hours-up fa fas fa-chevron-up'
22188 cls: 'minutes-up fa fas fa-chevron-up'
22209 cls: 'timepicker-hour',
22224 cls: 'timepicker-minute',
22239 cls: 'btn btn-primary period',
22261 cls: 'hours-down fa fas fa-chevron-down'
22281 cls: 'minutes-down fa fas fa-chevron-down'
22299 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22306 var hours = this.time.getHours();
22307 var minutes = this.time.getMinutes();
22320 hours = hours - 12;
22324 hours = '0' + hours;
22328 minutes = '0' + minutes;
22331 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22332 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22333 this.pop.select('button', true).first().dom.innerHTML = period;
22339 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22341 var cls = ['bottom'];
22343 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22350 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22354 //this.picker().setXY(20000,20000);
22355 this.picker().addClass(cls.join('-'));
22359 Roo.each(cls, function(c){
22364 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22365 //_this.picker().setTop(_this.inputEl().getHeight());
22369 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22371 //_this.picker().setTop(0 - _this.picker().getHeight());
22376 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22380 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22388 onFocus : function()
22390 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22394 onBlur : function()
22396 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22402 this.picker().show();
22407 this.fireEvent('show', this, this.date);
22412 this.picker().hide();
22415 this.fireEvent('hide', this, this.date);
22418 setTime : function()
22421 this.setValue(this.time.format(this.format));
22423 this.fireEvent('select', this, this.date);
22428 onMousedown: function(e){
22429 e.stopPropagation();
22430 e.preventDefault();
22433 onIncrementHours: function()
22435 Roo.log('onIncrementHours');
22436 this.time = this.time.add(Date.HOUR, 1);
22441 onDecrementHours: function()
22443 Roo.log('onDecrementHours');
22444 this.time = this.time.add(Date.HOUR, -1);
22448 onIncrementMinutes: function()
22450 Roo.log('onIncrementMinutes');
22451 this.time = this.time.add(Date.MINUTE, 1);
22455 onDecrementMinutes: function()
22457 Roo.log('onDecrementMinutes');
22458 this.time = this.time.add(Date.MINUTE, -1);
22462 onTogglePeriod: function()
22464 Roo.log('onTogglePeriod');
22465 this.time = this.time.add(Date.HOUR, 12);
22473 Roo.apply(Roo.bootstrap.TimeField, {
22477 cls: 'datepicker dropdown-menu',
22481 cls: 'datepicker-time',
22485 cls: 'table-condensed',
22514 cls: 'btn btn-info ok',
22542 * @class Roo.bootstrap.MonthField
22543 * @extends Roo.bootstrap.Input
22544 * Bootstrap MonthField class
22546 * @cfg {String} language default en
22549 * Create a new MonthField
22550 * @param {Object} config The config object
22553 Roo.bootstrap.MonthField = function(config){
22554 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22559 * Fires when this field show.
22560 * @param {Roo.bootstrap.MonthField} this
22561 * @param {Mixed} date The date value
22566 * Fires when this field hide.
22567 * @param {Roo.bootstrap.MonthField} this
22568 * @param {Mixed} date The date value
22573 * Fires when select a date.
22574 * @param {Roo.bootstrap.MonthField} this
22575 * @param {String} oldvalue The old value
22576 * @param {String} newvalue The new value
22582 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22584 onRender: function(ct, position)
22587 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22589 this.language = this.language || 'en';
22590 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22591 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22593 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22594 this.isInline = false;
22595 this.isInput = true;
22596 this.component = this.el.select('.add-on', true).first() || false;
22597 this.component = (this.component && this.component.length === 0) ? false : this.component;
22598 this.hasInput = this.component && this.inputEL().length;
22600 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22602 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22604 this.picker().on('mousedown', this.onMousedown, this);
22605 this.picker().on('click', this.onClick, this);
22607 this.picker().addClass('datepicker-dropdown');
22609 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22610 v.setStyle('width', '189px');
22617 if(this.isInline) {
22623 setValue: function(v, suppressEvent)
22625 var o = this.getValue();
22627 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22631 if(suppressEvent !== true){
22632 this.fireEvent('select', this, o, v);
22637 getValue: function()
22642 onClick: function(e)
22644 e.stopPropagation();
22645 e.preventDefault();
22647 var target = e.getTarget();
22649 if(target.nodeName.toLowerCase() === 'i'){
22650 target = Roo.get(target).dom.parentNode;
22653 var nodeName = target.nodeName;
22654 var className = target.className;
22655 var html = target.innerHTML;
22657 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22661 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22663 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22669 picker : function()
22671 return this.pickerEl;
22674 fillMonths: function()
22677 var months = this.picker().select('>.datepicker-months td', true).first();
22679 months.dom.innerHTML = '';
22685 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22688 months.createChild(month);
22697 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22698 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22701 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22702 e.removeClass('active');
22704 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22705 e.addClass('active');
22712 if(this.isInline) {
22716 this.picker().removeClass(['bottom', 'top']);
22718 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22720 * place to the top of element!
22724 this.picker().addClass('top');
22725 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22730 this.picker().addClass('bottom');
22732 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22735 onFocus : function()
22737 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22741 onBlur : function()
22743 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22745 var d = this.inputEl().getValue();
22754 this.picker().show();
22755 this.picker().select('>.datepicker-months', true).first().show();
22759 this.fireEvent('show', this, this.date);
22764 if(this.isInline) {
22767 this.picker().hide();
22768 this.fireEvent('hide', this, this.date);
22772 onMousedown: function(e)
22774 e.stopPropagation();
22775 e.preventDefault();
22780 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22784 fireKey: function(e)
22786 if (!this.picker().isVisible()){
22787 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22798 e.preventDefault();
22802 dir = e.keyCode == 37 ? -1 : 1;
22804 this.vIndex = this.vIndex + dir;
22806 if(this.vIndex < 0){
22810 if(this.vIndex > 11){
22814 if(isNaN(this.vIndex)){
22818 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22824 dir = e.keyCode == 38 ? -1 : 1;
22826 this.vIndex = this.vIndex + dir * 4;
22828 if(this.vIndex < 0){
22832 if(this.vIndex > 11){
22836 if(isNaN(this.vIndex)){
22840 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22845 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22846 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22850 e.preventDefault();
22853 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22854 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22870 this.picker().remove();
22875 Roo.apply(Roo.bootstrap.MonthField, {
22894 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22895 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22900 Roo.apply(Roo.bootstrap.MonthField, {
22904 cls: 'datepicker dropdown-menu roo-dynamic',
22908 cls: 'datepicker-months',
22912 cls: 'table-condensed',
22914 Roo.bootstrap.DateField.content
22934 * @class Roo.bootstrap.CheckBox
22935 * @extends Roo.bootstrap.Input
22936 * Bootstrap CheckBox class
22938 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22939 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22940 * @cfg {String} boxLabel The text that appears beside the checkbox
22941 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22942 * @cfg {Boolean} checked initnal the element
22943 * @cfg {Boolean} inline inline the element (default false)
22944 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22945 * @cfg {String} tooltip label tooltip
22948 * Create a new CheckBox
22949 * @param {Object} config The config object
22952 Roo.bootstrap.CheckBox = function(config){
22953 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22958 * Fires when the element is checked or unchecked.
22959 * @param {Roo.bootstrap.CheckBox} this This input
22960 * @param {Boolean} checked The new checked value
22965 * Fires when the element is click.
22966 * @param {Roo.bootstrap.CheckBox} this This input
22973 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22975 inputType: 'checkbox',
22984 // checkbox success does not make any sense really..
22989 getAutoCreate : function()
22991 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22997 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23000 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23006 type : this.inputType,
23007 value : this.inputValue,
23008 cls : 'roo-' + this.inputType, //'form-box',
23009 placeholder : this.placeholder || ''
23013 if(this.inputType != 'radio'){
23017 cls : 'roo-hidden-value',
23018 value : this.checked ? this.inputValue : this.valueOff
23023 if (this.weight) { // Validity check?
23024 cfg.cls += " " + this.inputType + "-" + this.weight;
23027 if (this.disabled) {
23028 input.disabled=true;
23032 input.checked = this.checked;
23037 input.name = this.name;
23039 if(this.inputType != 'radio'){
23040 hidden.name = this.name;
23041 input.name = '_hidden_' + this.name;
23046 input.cls += ' input-' + this.size;
23051 ['xs','sm','md','lg'].map(function(size){
23052 if (settings[size]) {
23053 cfg.cls += ' col-' + size + '-' + settings[size];
23057 var inputblock = input;
23059 if (this.before || this.after) {
23062 cls : 'input-group',
23067 inputblock.cn.push({
23069 cls : 'input-group-addon',
23074 inputblock.cn.push(input);
23076 if(this.inputType != 'radio'){
23077 inputblock.cn.push(hidden);
23081 inputblock.cn.push({
23083 cls : 'input-group-addon',
23089 var boxLabelCfg = false;
23095 //'for': id, // box label is handled by onclick - so no for...
23097 html: this.boxLabel
23100 boxLabelCfg.tooltip = this.tooltip;
23106 if (align ==='left' && this.fieldLabel.length) {
23107 // Roo.log("left and has label");
23112 cls : 'control-label',
23113 html : this.fieldLabel
23124 cfg.cn[1].cn.push(boxLabelCfg);
23127 if(this.labelWidth > 12){
23128 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23131 if(this.labelWidth < 13 && this.labelmd == 0){
23132 this.labelmd = this.labelWidth;
23135 if(this.labellg > 0){
23136 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23137 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23140 if(this.labelmd > 0){
23141 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23142 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23145 if(this.labelsm > 0){
23146 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23147 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23150 if(this.labelxs > 0){
23151 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23152 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23155 } else if ( this.fieldLabel.length) {
23156 // Roo.log(" label");
23160 tag: this.boxLabel ? 'span' : 'label',
23162 cls: 'control-label box-input-label',
23163 //cls : 'input-group-addon',
23164 html : this.fieldLabel
23171 cfg.cn.push(boxLabelCfg);
23176 // Roo.log(" no label && no align");
23177 cfg.cn = [ inputblock ] ;
23179 cfg.cn.push(boxLabelCfg);
23187 if(this.inputType != 'radio'){
23188 cfg.cn.push(hidden);
23196 * return the real input element.
23198 inputEl: function ()
23200 return this.el.select('input.roo-' + this.inputType,true).first();
23202 hiddenEl: function ()
23204 return this.el.select('input.roo-hidden-value',true).first();
23207 labelEl: function()
23209 return this.el.select('label.control-label',true).first();
23211 /* depricated... */
23215 return this.labelEl();
23218 boxLabelEl: function()
23220 return this.el.select('label.box-label',true).first();
23223 initEvents : function()
23225 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23227 this.inputEl().on('click', this.onClick, this);
23229 if (this.boxLabel) {
23230 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23233 this.startValue = this.getValue();
23236 Roo.bootstrap.CheckBox.register(this);
23240 onClick : function(e)
23242 if(this.fireEvent('click', this, e) !== false){
23243 this.setChecked(!this.checked);
23248 setChecked : function(state,suppressEvent)
23250 this.startValue = this.getValue();
23252 if(this.inputType == 'radio'){
23254 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23255 e.dom.checked = false;
23258 this.inputEl().dom.checked = true;
23260 this.inputEl().dom.value = this.inputValue;
23262 if(suppressEvent !== true){
23263 this.fireEvent('check', this, true);
23271 this.checked = state;
23273 this.inputEl().dom.checked = state;
23276 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23278 if(suppressEvent !== true){
23279 this.fireEvent('check', this, state);
23285 getValue : function()
23287 if(this.inputType == 'radio'){
23288 return this.getGroupValue();
23291 return this.hiddenEl().dom.value;
23295 getGroupValue : function()
23297 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23301 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23304 setValue : function(v,suppressEvent)
23306 if(this.inputType == 'radio'){
23307 this.setGroupValue(v, suppressEvent);
23311 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23316 setGroupValue : function(v, suppressEvent)
23318 this.startValue = this.getValue();
23320 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23321 e.dom.checked = false;
23323 if(e.dom.value == v){
23324 e.dom.checked = true;
23328 if(suppressEvent !== true){
23329 this.fireEvent('check', this, true);
23337 validate : function()
23339 if(this.getVisibilityEl().hasClass('hidden')){
23345 (this.inputType == 'radio' && this.validateRadio()) ||
23346 (this.inputType == 'checkbox' && this.validateCheckbox())
23352 this.markInvalid();
23356 validateRadio : function()
23358 if(this.getVisibilityEl().hasClass('hidden')){
23362 if(this.allowBlank){
23368 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23369 if(!e.dom.checked){
23381 validateCheckbox : function()
23384 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23385 //return (this.getValue() == this.inputValue) ? true : false;
23388 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23396 for(var i in group){
23397 if(group[i].el.isVisible(true)){
23405 for(var i in group){
23410 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23417 * Mark this field as valid
23419 markValid : function()
23423 this.fireEvent('valid', this);
23425 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23428 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23435 if(this.inputType == 'radio'){
23436 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23437 var fg = e.findParent('.form-group', false, true);
23438 if (Roo.bootstrap.version == 3) {
23439 fg.removeClass([_this.invalidClass, _this.validClass]);
23440 fg.addClass(_this.validClass);
23442 fg.removeClass(['is-valid', 'is-invalid']);
23443 fg.addClass('is-valid');
23451 var fg = this.el.findParent('.form-group', false, true);
23452 if (Roo.bootstrap.version == 3) {
23453 fg.removeClass([this.invalidClass, this.validClass]);
23454 fg.addClass(this.validClass);
23456 fg.removeClass(['is-valid', 'is-invalid']);
23457 fg.addClass('is-valid');
23462 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23468 for(var i in group){
23469 var fg = group[i].el.findParent('.form-group', false, true);
23470 if (Roo.bootstrap.version == 3) {
23471 fg.removeClass([this.invalidClass, this.validClass]);
23472 fg.addClass(this.validClass);
23474 fg.removeClass(['is-valid', 'is-invalid']);
23475 fg.addClass('is-valid');
23481 * Mark this field as invalid
23482 * @param {String} msg The validation message
23484 markInvalid : function(msg)
23486 if(this.allowBlank){
23492 this.fireEvent('invalid', this, msg);
23494 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23497 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23501 label.markInvalid();
23504 if(this.inputType == 'radio'){
23506 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23507 var fg = e.findParent('.form-group', false, true);
23508 if (Roo.bootstrap.version == 3) {
23509 fg.removeClass([_this.invalidClass, _this.validClass]);
23510 fg.addClass(_this.invalidClass);
23512 fg.removeClass(['is-invalid', 'is-valid']);
23513 fg.addClass('is-invalid');
23521 var fg = this.el.findParent('.form-group', false, true);
23522 if (Roo.bootstrap.version == 3) {
23523 fg.removeClass([_this.invalidClass, _this.validClass]);
23524 fg.addClass(_this.invalidClass);
23526 fg.removeClass(['is-invalid', 'is-valid']);
23527 fg.addClass('is-invalid');
23532 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23538 for(var i in group){
23539 var fg = group[i].el.findParent('.form-group', false, true);
23540 if (Roo.bootstrap.version == 3) {
23541 fg.removeClass([_this.invalidClass, _this.validClass]);
23542 fg.addClass(_this.invalidClass);
23544 fg.removeClass(['is-invalid', 'is-valid']);
23545 fg.addClass('is-invalid');
23551 clearInvalid : function()
23553 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23555 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23557 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23559 if (label && label.iconEl) {
23560 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23561 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23565 disable : function()
23567 if(this.inputType != 'radio'){
23568 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23575 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23576 _this.getActionEl().addClass(this.disabledClass);
23577 e.dom.disabled = true;
23581 this.disabled = true;
23582 this.fireEvent("disable", this);
23586 enable : function()
23588 if(this.inputType != 'radio'){
23589 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23596 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23597 _this.getActionEl().removeClass(this.disabledClass);
23598 e.dom.disabled = false;
23602 this.disabled = false;
23603 this.fireEvent("enable", this);
23607 setBoxLabel : function(v)
23612 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23618 Roo.apply(Roo.bootstrap.CheckBox, {
23623 * register a CheckBox Group
23624 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23626 register : function(checkbox)
23628 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23629 this.groups[checkbox.groupId] = {};
23632 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23636 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23640 * fetch a CheckBox Group based on the group ID
23641 * @param {string} the group ID
23642 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23644 get: function(groupId) {
23645 if (typeof(this.groups[groupId]) == 'undefined') {
23649 return this.groups[groupId] ;
23662 * @class Roo.bootstrap.Radio
23663 * @extends Roo.bootstrap.Component
23664 * Bootstrap Radio class
23665 * @cfg {String} boxLabel - the label associated
23666 * @cfg {String} value - the value of radio
23669 * Create a new Radio
23670 * @param {Object} config The config object
23672 Roo.bootstrap.Radio = function(config){
23673 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23677 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23683 getAutoCreate : function()
23687 cls : 'form-group radio',
23692 html : this.boxLabel
23700 initEvents : function()
23702 this.parent().register(this);
23704 this.el.on('click', this.onClick, this);
23708 onClick : function(e)
23710 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23711 this.setChecked(true);
23715 setChecked : function(state, suppressEvent)
23717 this.parent().setValue(this.value, suppressEvent);
23721 setBoxLabel : function(v)
23726 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23741 * @class Roo.bootstrap.SecurePass
23742 * @extends Roo.bootstrap.Input
23743 * Bootstrap SecurePass class
23747 * Create a new SecurePass
23748 * @param {Object} config The config object
23751 Roo.bootstrap.SecurePass = function (config) {
23752 // these go here, so the translation tool can replace them..
23754 PwdEmpty: "Please type a password, and then retype it to confirm.",
23755 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23756 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23757 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23758 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23759 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23760 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23761 TooWeak: "Your password is Too Weak."
23763 this.meterLabel = "Password strength:";
23764 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23765 this.meterClass = [
23766 "roo-password-meter-tooweak",
23767 "roo-password-meter-weak",
23768 "roo-password-meter-medium",
23769 "roo-password-meter-strong",
23770 "roo-password-meter-grey"
23775 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23778 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23780 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23782 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23783 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23784 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23785 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23786 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23787 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23788 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23798 * @cfg {String/Object} Label for the strength meter (defaults to
23799 * 'Password strength:')
23804 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23805 * ['Weak', 'Medium', 'Strong'])
23808 pwdStrengths: false,
23821 initEvents: function ()
23823 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23825 if (this.el.is('input[type=password]') && Roo.isSafari) {
23826 this.el.on('keydown', this.SafariOnKeyDown, this);
23829 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23832 onRender: function (ct, position)
23834 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23835 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23836 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23838 this.trigger.createChild({
23843 cls: 'roo-password-meter-grey col-xs-12',
23846 //width: this.meterWidth + 'px'
23850 cls: 'roo-password-meter-text'
23856 if (this.hideTrigger) {
23857 this.trigger.setDisplayed(false);
23859 this.setSize(this.width || '', this.height || '');
23862 onDestroy: function ()
23864 if (this.trigger) {
23865 this.trigger.removeAllListeners();
23866 this.trigger.remove();
23869 this.wrap.remove();
23871 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23874 checkStrength: function ()
23876 var pwd = this.inputEl().getValue();
23877 if (pwd == this._lastPwd) {
23882 if (this.ClientSideStrongPassword(pwd)) {
23884 } else if (this.ClientSideMediumPassword(pwd)) {
23886 } else if (this.ClientSideWeakPassword(pwd)) {
23892 Roo.log('strength1: ' + strength);
23894 //var pm = this.trigger.child('div/div/div').dom;
23895 var pm = this.trigger.child('div/div');
23896 pm.removeClass(this.meterClass);
23897 pm.addClass(this.meterClass[strength]);
23900 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23902 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23904 this._lastPwd = pwd;
23908 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23910 this._lastPwd = '';
23912 var pm = this.trigger.child('div/div');
23913 pm.removeClass(this.meterClass);
23914 pm.addClass('roo-password-meter-grey');
23917 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23920 this.inputEl().dom.type='password';
23923 validateValue: function (value)
23925 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23928 if (value.length == 0) {
23929 if (this.allowBlank) {
23930 this.clearInvalid();
23934 this.markInvalid(this.errors.PwdEmpty);
23935 this.errorMsg = this.errors.PwdEmpty;
23943 if (!value.match(/[\x21-\x7e]+/)) {
23944 this.markInvalid(this.errors.PwdBadChar);
23945 this.errorMsg = this.errors.PwdBadChar;
23948 if (value.length < 6) {
23949 this.markInvalid(this.errors.PwdShort);
23950 this.errorMsg = this.errors.PwdShort;
23953 if (value.length > 16) {
23954 this.markInvalid(this.errors.PwdLong);
23955 this.errorMsg = this.errors.PwdLong;
23959 if (this.ClientSideStrongPassword(value)) {
23961 } else if (this.ClientSideMediumPassword(value)) {
23963 } else if (this.ClientSideWeakPassword(value)) {
23970 if (strength < 2) {
23971 //this.markInvalid(this.errors.TooWeak);
23972 this.errorMsg = this.errors.TooWeak;
23977 console.log('strength2: ' + strength);
23979 //var pm = this.trigger.child('div/div/div').dom;
23981 var pm = this.trigger.child('div/div');
23982 pm.removeClass(this.meterClass);
23983 pm.addClass(this.meterClass[strength]);
23985 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23987 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23989 this.errorMsg = '';
23993 CharacterSetChecks: function (type)
23996 this.fResult = false;
23999 isctype: function (character, type)
24002 case this.kCapitalLetter:
24003 if (character >= 'A' && character <= 'Z') {
24008 case this.kSmallLetter:
24009 if (character >= 'a' && character <= 'z') {
24015 if (character >= '0' && character <= '9') {
24020 case this.kPunctuation:
24021 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24032 IsLongEnough: function (pwd, size)
24034 return !(pwd == null || isNaN(size) || pwd.length < size);
24037 SpansEnoughCharacterSets: function (word, nb)
24039 if (!this.IsLongEnough(word, nb))
24044 var characterSetChecks = new Array(
24045 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24046 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24049 for (var index = 0; index < word.length; ++index) {
24050 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24051 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24052 characterSetChecks[nCharSet].fResult = true;
24059 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24060 if (characterSetChecks[nCharSet].fResult) {
24065 if (nCharSets < nb) {
24071 ClientSideStrongPassword: function (pwd)
24073 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24076 ClientSideMediumPassword: function (pwd)
24078 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24081 ClientSideWeakPassword: function (pwd)
24083 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24086 })//<script type="text/javascript">
24089 * Based Ext JS Library 1.1.1
24090 * Copyright(c) 2006-2007, Ext JS, LLC.
24096 * @class Roo.HtmlEditorCore
24097 * @extends Roo.Component
24098 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24100 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24103 Roo.HtmlEditorCore = function(config){
24106 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24111 * @event initialize
24112 * Fires when the editor is fully initialized (including the iframe)
24113 * @param {Roo.HtmlEditorCore} this
24118 * Fires when the editor is first receives the focus. Any insertion must wait
24119 * until after this event.
24120 * @param {Roo.HtmlEditorCore} this
24124 * @event beforesync
24125 * Fires before the textarea is updated with content from the editor iframe. Return false
24126 * to cancel the sync.
24127 * @param {Roo.HtmlEditorCore} this
24128 * @param {String} html
24132 * @event beforepush
24133 * Fires before the iframe editor is updated with content from the textarea. Return false
24134 * to cancel the push.
24135 * @param {Roo.HtmlEditorCore} this
24136 * @param {String} html
24141 * Fires when the textarea is updated with content from the editor iframe.
24142 * @param {Roo.HtmlEditorCore} this
24143 * @param {String} html
24148 * Fires when the iframe editor is updated with content from the textarea.
24149 * @param {Roo.HtmlEditorCore} this
24150 * @param {String} html
24155 * @event editorevent
24156 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24157 * @param {Roo.HtmlEditorCore} this
24163 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24165 // defaults : white / black...
24166 this.applyBlacklists();
24173 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24177 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24183 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24188 * @cfg {Number} height (in pixels)
24192 * @cfg {Number} width (in pixels)
24197 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24200 stylesheets: false,
24205 // private properties
24206 validationEvent : false,
24208 initialized : false,
24210 sourceEditMode : false,
24211 onFocus : Roo.emptyFn,
24213 hideMode:'offsets',
24217 // blacklist + whitelisted elements..
24224 * Protected method that will not generally be called directly. It
24225 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24226 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24228 getDocMarkup : function(){
24232 // inherit styels from page...??
24233 if (this.stylesheets === false) {
24235 Roo.get(document.head).select('style').each(function(node) {
24236 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24239 Roo.get(document.head).select('link').each(function(node) {
24240 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24243 } else if (!this.stylesheets.length) {
24245 st = '<style type="text/css">' +
24246 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24249 for (var i in this.stylesheets) {
24250 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24255 st += '<style type="text/css">' +
24256 'IMG { cursor: pointer } ' +
24259 var cls = 'roo-htmleditor-body';
24261 if(this.bodyCls.length){
24262 cls += ' ' + this.bodyCls;
24265 return '<html><head>' + st +
24266 //<style type="text/css">' +
24267 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24269 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24273 onRender : function(ct, position)
24276 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24277 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24280 this.el.dom.style.border = '0 none';
24281 this.el.dom.setAttribute('tabIndex', -1);
24282 this.el.addClass('x-hidden hide');
24286 if(Roo.isIE){ // fix IE 1px bogus margin
24287 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24291 this.frameId = Roo.id();
24295 var iframe = this.owner.wrap.createChild({
24297 cls: 'form-control', // bootstrap..
24299 name: this.frameId,
24300 frameBorder : 'no',
24301 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24306 this.iframe = iframe.dom;
24308 this.assignDocWin();
24310 this.doc.designMode = 'on';
24313 this.doc.write(this.getDocMarkup());
24317 var task = { // must defer to wait for browser to be ready
24319 //console.log("run task?" + this.doc.readyState);
24320 this.assignDocWin();
24321 if(this.doc.body || this.doc.readyState == 'complete'){
24323 this.doc.designMode="on";
24327 Roo.TaskMgr.stop(task);
24328 this.initEditor.defer(10, this);
24335 Roo.TaskMgr.start(task);
24340 onResize : function(w, h)
24342 Roo.log('resize: ' +w + ',' + h );
24343 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24347 if(typeof w == 'number'){
24349 this.iframe.style.width = w + 'px';
24351 if(typeof h == 'number'){
24353 this.iframe.style.height = h + 'px';
24355 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24362 * Toggles the editor between standard and source edit mode.
24363 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24365 toggleSourceEdit : function(sourceEditMode){
24367 this.sourceEditMode = sourceEditMode === true;
24369 if(this.sourceEditMode){
24371 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24374 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24375 //this.iframe.className = '';
24378 //this.setSize(this.owner.wrap.getSize());
24379 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24386 * Protected method that will not generally be called directly. If you need/want
24387 * custom HTML cleanup, this is the method you should override.
24388 * @param {String} html The HTML to be cleaned
24389 * return {String} The cleaned HTML
24391 cleanHtml : function(html){
24392 html = String(html);
24393 if(html.length > 5){
24394 if(Roo.isSafari){ // strip safari nonsense
24395 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24398 if(html == ' '){
24405 * HTML Editor -> Textarea
24406 * Protected method that will not generally be called directly. Syncs the contents
24407 * of the editor iframe with the textarea.
24409 syncValue : function(){
24410 if(this.initialized){
24411 var bd = (this.doc.body || this.doc.documentElement);
24412 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24413 var html = bd.innerHTML;
24415 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24416 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24418 html = '<div style="'+m[0]+'">' + html + '</div>';
24421 html = this.cleanHtml(html);
24422 // fix up the special chars.. normaly like back quotes in word...
24423 // however we do not want to do this with chinese..
24424 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24426 var cc = match.charCodeAt();
24428 // Get the character value, handling surrogate pairs
24429 if (match.length == 2) {
24430 // It's a surrogate pair, calculate the Unicode code point
24431 var high = match.charCodeAt(0) - 0xD800;
24432 var low = match.charCodeAt(1) - 0xDC00;
24433 cc = (high * 0x400) + low + 0x10000;
24435 (cc >= 0x4E00 && cc < 0xA000 ) ||
24436 (cc >= 0x3400 && cc < 0x4E00 ) ||
24437 (cc >= 0xf900 && cc < 0xfb00 )
24442 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24443 return "&#" + cc + ";";
24450 if(this.owner.fireEvent('beforesync', this, html) !== false){
24451 this.el.dom.value = html;
24452 this.owner.fireEvent('sync', this, html);
24458 * Protected method that will not generally be called directly. Pushes the value of the textarea
24459 * into the iframe editor.
24461 pushValue : function(){
24462 if(this.initialized){
24463 var v = this.el.dom.value.trim();
24465 // if(v.length < 1){
24469 if(this.owner.fireEvent('beforepush', this, v) !== false){
24470 var d = (this.doc.body || this.doc.documentElement);
24472 this.cleanUpPaste();
24473 this.el.dom.value = d.innerHTML;
24474 this.owner.fireEvent('push', this, v);
24480 deferFocus : function(){
24481 this.focus.defer(10, this);
24485 focus : function(){
24486 if(this.win && !this.sourceEditMode){
24493 assignDocWin: function()
24495 var iframe = this.iframe;
24498 this.doc = iframe.contentWindow.document;
24499 this.win = iframe.contentWindow;
24501 // if (!Roo.get(this.frameId)) {
24504 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24505 // this.win = Roo.get(this.frameId).dom.contentWindow;
24507 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24511 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24512 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24517 initEditor : function(){
24518 //console.log("INIT EDITOR");
24519 this.assignDocWin();
24523 this.doc.designMode="on";
24525 this.doc.write(this.getDocMarkup());
24528 var dbody = (this.doc.body || this.doc.documentElement);
24529 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24530 // this copies styles from the containing element into thsi one..
24531 // not sure why we need all of this..
24532 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24534 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24535 //ss['background-attachment'] = 'fixed'; // w3c
24536 dbody.bgProperties = 'fixed'; // ie
24537 //Roo.DomHelper.applyStyles(dbody, ss);
24538 Roo.EventManager.on(this.doc, {
24539 //'mousedown': this.onEditorEvent,
24540 'mouseup': this.onEditorEvent,
24541 'dblclick': this.onEditorEvent,
24542 'click': this.onEditorEvent,
24543 'keyup': this.onEditorEvent,
24548 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24550 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24551 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24553 this.initialized = true;
24555 this.owner.fireEvent('initialize', this);
24560 onDestroy : function(){
24566 //for (var i =0; i < this.toolbars.length;i++) {
24567 // // fixme - ask toolbars for heights?
24568 // this.toolbars[i].onDestroy();
24571 //this.wrap.dom.innerHTML = '';
24572 //this.wrap.remove();
24577 onFirstFocus : function(){
24579 this.assignDocWin();
24582 this.activated = true;
24585 if(Roo.isGecko){ // prevent silly gecko errors
24587 var s = this.win.getSelection();
24588 if(!s.focusNode || s.focusNode.nodeType != 3){
24589 var r = s.getRangeAt(0);
24590 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24595 this.execCmd('useCSS', true);
24596 this.execCmd('styleWithCSS', false);
24599 this.owner.fireEvent('activate', this);
24603 adjustFont: function(btn){
24604 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24605 //if(Roo.isSafari){ // safari
24608 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24609 if(Roo.isSafari){ // safari
24610 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24611 v = (v < 10) ? 10 : v;
24612 v = (v > 48) ? 48 : v;
24613 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24618 v = Math.max(1, v+adjust);
24620 this.execCmd('FontSize', v );
24623 onEditorEvent : function(e)
24625 this.owner.fireEvent('editorevent', this, e);
24626 // this.updateToolbar();
24627 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24630 insertTag : function(tg)
24632 // could be a bit smarter... -> wrap the current selected tRoo..
24633 if (tg.toLowerCase() == 'span' ||
24634 tg.toLowerCase() == 'code' ||
24635 tg.toLowerCase() == 'sup' ||
24636 tg.toLowerCase() == 'sub'
24639 range = this.createRange(this.getSelection());
24640 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24641 wrappingNode.appendChild(range.extractContents());
24642 range.insertNode(wrappingNode);
24649 this.execCmd("formatblock", tg);
24653 insertText : function(txt)
24657 var range = this.createRange();
24658 range.deleteContents();
24659 //alert(Sender.getAttribute('label'));
24661 range.insertNode(this.doc.createTextNode(txt));
24667 * Executes a Midas editor command on the editor document and performs necessary focus and
24668 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24669 * @param {String} cmd The Midas command
24670 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24672 relayCmd : function(cmd, value){
24674 this.execCmd(cmd, value);
24675 this.owner.fireEvent('editorevent', this);
24676 //this.updateToolbar();
24677 this.owner.deferFocus();
24681 * Executes a Midas editor command directly on the editor document.
24682 * For visual commands, you should use {@link #relayCmd} instead.
24683 * <b>This should only be called after the editor is initialized.</b>
24684 * @param {String} cmd The Midas command
24685 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24687 execCmd : function(cmd, value){
24688 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24695 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24697 * @param {String} text | dom node..
24699 insertAtCursor : function(text)
24702 if(!this.activated){
24708 var r = this.doc.selection.createRange();
24719 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24723 // from jquery ui (MIT licenced)
24725 var win = this.win;
24727 if (win.getSelection && win.getSelection().getRangeAt) {
24728 range = win.getSelection().getRangeAt(0);
24729 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24730 range.insertNode(node);
24731 } else if (win.document.selection && win.document.selection.createRange) {
24732 // no firefox support
24733 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24734 win.document.selection.createRange().pasteHTML(txt);
24736 // no firefox support
24737 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24738 this.execCmd('InsertHTML', txt);
24747 mozKeyPress : function(e){
24749 var c = e.getCharCode(), cmd;
24752 c = String.fromCharCode(c).toLowerCase();
24766 this.cleanUpPaste.defer(100, this);
24774 e.preventDefault();
24782 fixKeys : function(){ // load time branching for fastest keydown performance
24784 return function(e){
24785 var k = e.getKey(), r;
24788 r = this.doc.selection.createRange();
24791 r.pasteHTML('    ');
24798 r = this.doc.selection.createRange();
24800 var target = r.parentElement();
24801 if(!target || target.tagName.toLowerCase() != 'li'){
24803 r.pasteHTML('<br />');
24809 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24810 this.cleanUpPaste.defer(100, this);
24816 }else if(Roo.isOpera){
24817 return function(e){
24818 var k = e.getKey();
24822 this.execCmd('InsertHTML','    ');
24825 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24826 this.cleanUpPaste.defer(100, this);
24831 }else if(Roo.isSafari){
24832 return function(e){
24833 var k = e.getKey();
24837 this.execCmd('InsertText','\t');
24841 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24842 this.cleanUpPaste.defer(100, this);
24850 getAllAncestors: function()
24852 var p = this.getSelectedNode();
24855 a.push(p); // push blank onto stack..
24856 p = this.getParentElement();
24860 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24864 a.push(this.doc.body);
24868 lastSelNode : false,
24871 getSelection : function()
24873 this.assignDocWin();
24874 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24877 getSelectedNode: function()
24879 // this may only work on Gecko!!!
24881 // should we cache this!!!!
24886 var range = this.createRange(this.getSelection()).cloneRange();
24889 var parent = range.parentElement();
24891 var testRange = range.duplicate();
24892 testRange.moveToElementText(parent);
24893 if (testRange.inRange(range)) {
24896 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24899 parent = parent.parentElement;
24904 // is ancestor a text element.
24905 var ac = range.commonAncestorContainer;
24906 if (ac.nodeType == 3) {
24907 ac = ac.parentNode;
24910 var ar = ac.childNodes;
24913 var other_nodes = [];
24914 var has_other_nodes = false;
24915 for (var i=0;i<ar.length;i++) {
24916 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24919 // fullly contained node.
24921 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24926 // probably selected..
24927 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24928 other_nodes.push(ar[i]);
24932 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24937 has_other_nodes = true;
24939 if (!nodes.length && other_nodes.length) {
24940 nodes= other_nodes;
24942 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24948 createRange: function(sel)
24950 // this has strange effects when using with
24951 // top toolbar - not sure if it's a great idea.
24952 //this.editor.contentWindow.focus();
24953 if (typeof sel != "undefined") {
24955 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24957 return this.doc.createRange();
24960 return this.doc.createRange();
24963 getParentElement: function()
24966 this.assignDocWin();
24967 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24969 var range = this.createRange(sel);
24972 var p = range.commonAncestorContainer;
24973 while (p.nodeType == 3) { // text node
24984 * Range intersection.. the hard stuff...
24988 * [ -- selected range --- ]
24992 * if end is before start or hits it. fail.
24993 * if start is after end or hits it fail.
24995 * if either hits (but other is outside. - then it's not
25001 // @see http://www.thismuchiknow.co.uk/?p=64.
25002 rangeIntersectsNode : function(range, node)
25004 var nodeRange = node.ownerDocument.createRange();
25006 nodeRange.selectNode(node);
25008 nodeRange.selectNodeContents(node);
25011 var rangeStartRange = range.cloneRange();
25012 rangeStartRange.collapse(true);
25014 var rangeEndRange = range.cloneRange();
25015 rangeEndRange.collapse(false);
25017 var nodeStartRange = nodeRange.cloneRange();
25018 nodeStartRange.collapse(true);
25020 var nodeEndRange = nodeRange.cloneRange();
25021 nodeEndRange.collapse(false);
25023 return rangeStartRange.compareBoundaryPoints(
25024 Range.START_TO_START, nodeEndRange) == -1 &&
25025 rangeEndRange.compareBoundaryPoints(
25026 Range.START_TO_START, nodeStartRange) == 1;
25030 rangeCompareNode : function(range, node)
25032 var nodeRange = node.ownerDocument.createRange();
25034 nodeRange.selectNode(node);
25036 nodeRange.selectNodeContents(node);
25040 range.collapse(true);
25042 nodeRange.collapse(true);
25044 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25045 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25047 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25049 var nodeIsBefore = ss == 1;
25050 var nodeIsAfter = ee == -1;
25052 if (nodeIsBefore && nodeIsAfter) {
25055 if (!nodeIsBefore && nodeIsAfter) {
25056 return 1; //right trailed.
25059 if (nodeIsBefore && !nodeIsAfter) {
25060 return 2; // left trailed.
25066 // private? - in a new class?
25067 cleanUpPaste : function()
25069 // cleans up the whole document..
25070 Roo.log('cleanuppaste');
25072 this.cleanUpChildren(this.doc.body);
25073 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25074 if (clean != this.doc.body.innerHTML) {
25075 this.doc.body.innerHTML = clean;
25080 cleanWordChars : function(input) {// change the chars to hex code
25081 var he = Roo.HtmlEditorCore;
25083 var output = input;
25084 Roo.each(he.swapCodes, function(sw) {
25085 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25087 output = output.replace(swapper, sw[1]);
25094 cleanUpChildren : function (n)
25096 if (!n.childNodes.length) {
25099 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25100 this.cleanUpChild(n.childNodes[i]);
25107 cleanUpChild : function (node)
25110 //console.log(node);
25111 if (node.nodeName == "#text") {
25112 // clean up silly Windows -- stuff?
25115 if (node.nodeName == "#comment") {
25116 node.parentNode.removeChild(node);
25117 // clean up silly Windows -- stuff?
25120 var lcname = node.tagName.toLowerCase();
25121 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25122 // whitelist of tags..
25124 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25126 node.parentNode.removeChild(node);
25131 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25133 // spans with no attributes - just remove them..
25134 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25135 remove_keep_children = true;
25138 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25139 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25141 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25142 // remove_keep_children = true;
25145 if (remove_keep_children) {
25146 this.cleanUpChildren(node);
25147 // inserts everything just before this node...
25148 while (node.childNodes.length) {
25149 var cn = node.childNodes[0];
25150 node.removeChild(cn);
25151 node.parentNode.insertBefore(cn, node);
25153 node.parentNode.removeChild(node);
25157 if (!node.attributes || !node.attributes.length) {
25162 this.cleanUpChildren(node);
25166 function cleanAttr(n,v)
25169 if (v.match(/^\./) || v.match(/^\//)) {
25172 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25175 if (v.match(/^#/)) {
25178 if (v.match(/^\{/)) { // allow template editing.
25181 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25182 node.removeAttribute(n);
25186 var cwhite = this.cwhite;
25187 var cblack = this.cblack;
25189 function cleanStyle(n,v)
25191 if (v.match(/expression/)) { //XSS?? should we even bother..
25192 node.removeAttribute(n);
25196 var parts = v.split(/;/);
25199 Roo.each(parts, function(p) {
25200 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25204 var l = p.split(':').shift().replace(/\s+/g,'');
25205 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25207 if ( cwhite.length && cblack.indexOf(l) > -1) {
25208 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25209 //node.removeAttribute(n);
25213 // only allow 'c whitelisted system attributes'
25214 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25215 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25216 //node.removeAttribute(n);
25226 if (clean.length) {
25227 node.setAttribute(n, clean.join(';'));
25229 node.removeAttribute(n);
25235 for (var i = node.attributes.length-1; i > -1 ; i--) {
25236 var a = node.attributes[i];
25239 if (a.name.toLowerCase().substr(0,2)=='on') {
25240 node.removeAttribute(a.name);
25243 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25244 node.removeAttribute(a.name);
25247 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25248 cleanAttr(a.name,a.value); // fixme..
25251 if (a.name == 'style') {
25252 cleanStyle(a.name,a.value);
25255 /// clean up MS crap..
25256 // tecnically this should be a list of valid class'es..
25259 if (a.name == 'class') {
25260 if (a.value.match(/^Mso/)) {
25261 node.removeAttribute('class');
25264 if (a.value.match(/^body$/)) {
25265 node.removeAttribute('class');
25276 this.cleanUpChildren(node);
25282 * Clean up MS wordisms...
25284 cleanWord : function(node)
25287 this.cleanWord(this.doc.body);
25292 node.nodeName == 'SPAN' &&
25293 !node.hasAttributes() &&
25294 node.childNodes.length == 1 &&
25295 node.firstChild.nodeName == "#text"
25297 var textNode = node.firstChild;
25298 node.removeChild(textNode);
25299 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25300 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25302 node.parentNode.insertBefore(textNode, node);
25303 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25304 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25306 node.parentNode.removeChild(node);
25309 if (node.nodeName == "#text") {
25310 // clean up silly Windows -- stuff?
25313 if (node.nodeName == "#comment") {
25314 node.parentNode.removeChild(node);
25315 // clean up silly Windows -- stuff?
25319 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25320 node.parentNode.removeChild(node);
25323 //Roo.log(node.tagName);
25324 // remove - but keep children..
25325 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25326 //Roo.log('-- removed');
25327 while (node.childNodes.length) {
25328 var cn = node.childNodes[0];
25329 node.removeChild(cn);
25330 node.parentNode.insertBefore(cn, node);
25331 // move node to parent - and clean it..
25332 this.cleanWord(cn);
25334 node.parentNode.removeChild(node);
25335 /// no need to iterate chidlren = it's got none..
25336 //this.iterateChildren(node, this.cleanWord);
25340 if (node.className.length) {
25342 var cn = node.className.split(/\W+/);
25344 Roo.each(cn, function(cls) {
25345 if (cls.match(/Mso[a-zA-Z]+/)) {
25350 node.className = cna.length ? cna.join(' ') : '';
25352 node.removeAttribute("class");
25356 if (node.hasAttribute("lang")) {
25357 node.removeAttribute("lang");
25360 if (node.hasAttribute("style")) {
25362 var styles = node.getAttribute("style").split(";");
25364 Roo.each(styles, function(s) {
25365 if (!s.match(/:/)) {
25368 var kv = s.split(":");
25369 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25372 // what ever is left... we allow.
25375 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25376 if (!nstyle.length) {
25377 node.removeAttribute('style');
25380 this.iterateChildren(node, this.cleanWord);
25386 * iterateChildren of a Node, calling fn each time, using this as the scole..
25387 * @param {DomNode} node node to iterate children of.
25388 * @param {Function} fn method of this class to call on each item.
25390 iterateChildren : function(node, fn)
25392 if (!node.childNodes.length) {
25395 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25396 fn.call(this, node.childNodes[i])
25402 * cleanTableWidths.
25404 * Quite often pasting from word etc.. results in tables with column and widths.
25405 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25408 cleanTableWidths : function(node)
25413 this.cleanTableWidths(this.doc.body);
25418 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25421 Roo.log(node.tagName);
25422 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25423 this.iterateChildren(node, this.cleanTableWidths);
25426 if (node.hasAttribute('width')) {
25427 node.removeAttribute('width');
25431 if (node.hasAttribute("style")) {
25434 var styles = node.getAttribute("style").split(";");
25436 Roo.each(styles, function(s) {
25437 if (!s.match(/:/)) {
25440 var kv = s.split(":");
25441 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25444 // what ever is left... we allow.
25447 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25448 if (!nstyle.length) {
25449 node.removeAttribute('style');
25453 this.iterateChildren(node, this.cleanTableWidths);
25461 domToHTML : function(currentElement, depth, nopadtext) {
25463 depth = depth || 0;
25464 nopadtext = nopadtext || false;
25466 if (!currentElement) {
25467 return this.domToHTML(this.doc.body);
25470 //Roo.log(currentElement);
25472 var allText = false;
25473 var nodeName = currentElement.nodeName;
25474 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25476 if (nodeName == '#text') {
25478 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25483 if (nodeName != 'BODY') {
25486 // Prints the node tagName, such as <A>, <IMG>, etc
25489 for(i = 0; i < currentElement.attributes.length;i++) {
25491 var aname = currentElement.attributes.item(i).name;
25492 if (!currentElement.attributes.item(i).value.length) {
25495 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25498 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25507 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25510 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25515 // Traverse the tree
25517 var currentElementChild = currentElement.childNodes.item(i);
25518 var allText = true;
25519 var innerHTML = '';
25521 while (currentElementChild) {
25522 // Formatting code (indent the tree so it looks nice on the screen)
25523 var nopad = nopadtext;
25524 if (lastnode == 'SPAN') {
25528 if (currentElementChild.nodeName == '#text') {
25529 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25530 toadd = nopadtext ? toadd : toadd.trim();
25531 if (!nopad && toadd.length > 80) {
25532 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25534 innerHTML += toadd;
25537 currentElementChild = currentElement.childNodes.item(i);
25543 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25545 // Recursively traverse the tree structure of the child node
25546 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25547 lastnode = currentElementChild.nodeName;
25549 currentElementChild=currentElement.childNodes.item(i);
25555 // The remaining code is mostly for formatting the tree
25556 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25561 ret+= "</"+tagName+">";
25567 applyBlacklists : function()
25569 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25570 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25574 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25575 if (b.indexOf(tag) > -1) {
25578 this.white.push(tag);
25582 Roo.each(w, function(tag) {
25583 if (b.indexOf(tag) > -1) {
25586 if (this.white.indexOf(tag) > -1) {
25589 this.white.push(tag);
25594 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25595 if (w.indexOf(tag) > -1) {
25598 this.black.push(tag);
25602 Roo.each(b, function(tag) {
25603 if (w.indexOf(tag) > -1) {
25606 if (this.black.indexOf(tag) > -1) {
25609 this.black.push(tag);
25614 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25615 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25619 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25620 if (b.indexOf(tag) > -1) {
25623 this.cwhite.push(tag);
25627 Roo.each(w, function(tag) {
25628 if (b.indexOf(tag) > -1) {
25631 if (this.cwhite.indexOf(tag) > -1) {
25634 this.cwhite.push(tag);
25639 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25640 if (w.indexOf(tag) > -1) {
25643 this.cblack.push(tag);
25647 Roo.each(b, function(tag) {
25648 if (w.indexOf(tag) > -1) {
25651 if (this.cblack.indexOf(tag) > -1) {
25654 this.cblack.push(tag);
25659 setStylesheets : function(stylesheets)
25661 if(typeof(stylesheets) == 'string'){
25662 Roo.get(this.iframe.contentDocument.head).createChild({
25664 rel : 'stylesheet',
25673 Roo.each(stylesheets, function(s) {
25678 Roo.get(_this.iframe.contentDocument.head).createChild({
25680 rel : 'stylesheet',
25689 removeStylesheets : function()
25693 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25698 setStyle : function(style)
25700 Roo.get(this.iframe.contentDocument.head).createChild({
25709 // hide stuff that is not compatible
25723 * @event specialkey
25727 * @cfg {String} fieldClass @hide
25730 * @cfg {String} focusClass @hide
25733 * @cfg {String} autoCreate @hide
25736 * @cfg {String} inputType @hide
25739 * @cfg {String} invalidClass @hide
25742 * @cfg {String} invalidText @hide
25745 * @cfg {String} msgFx @hide
25748 * @cfg {String} validateOnBlur @hide
25752 Roo.HtmlEditorCore.white = [
25753 'area', 'br', 'img', 'input', 'hr', 'wbr',
25755 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25756 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25757 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25758 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25759 'table', 'ul', 'xmp',
25761 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25764 'dir', 'menu', 'ol', 'ul', 'dl',
25770 Roo.HtmlEditorCore.black = [
25771 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25773 'base', 'basefont', 'bgsound', 'blink', 'body',
25774 'frame', 'frameset', 'head', 'html', 'ilayer',
25775 'iframe', 'layer', 'link', 'meta', 'object',
25776 'script', 'style' ,'title', 'xml' // clean later..
25778 Roo.HtmlEditorCore.clean = [
25779 'script', 'style', 'title', 'xml'
25781 Roo.HtmlEditorCore.remove = [
25786 Roo.HtmlEditorCore.ablack = [
25790 Roo.HtmlEditorCore.aclean = [
25791 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25795 Roo.HtmlEditorCore.pwhite= [
25796 'http', 'https', 'mailto'
25799 // white listed style attributes.
25800 Roo.HtmlEditorCore.cwhite= [
25801 // 'text-align', /// default is to allow most things..
25807 // black listed style attributes.
25808 Roo.HtmlEditorCore.cblack= [
25809 // 'font-size' -- this can be set by the project
25813 Roo.HtmlEditorCore.swapCodes =[
25832 * @class Roo.bootstrap.HtmlEditor
25833 * @extends Roo.bootstrap.TextArea
25834 * Bootstrap HtmlEditor class
25837 * Create a new HtmlEditor
25838 * @param {Object} config The config object
25841 Roo.bootstrap.HtmlEditor = function(config){
25842 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25843 if (!this.toolbars) {
25844 this.toolbars = [];
25847 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25850 * @event initialize
25851 * Fires when the editor is fully initialized (including the iframe)
25852 * @param {HtmlEditor} this
25857 * Fires when the editor is first receives the focus. Any insertion must wait
25858 * until after this event.
25859 * @param {HtmlEditor} this
25863 * @event beforesync
25864 * Fires before the textarea is updated with content from the editor iframe. Return false
25865 * to cancel the sync.
25866 * @param {HtmlEditor} this
25867 * @param {String} html
25871 * @event beforepush
25872 * Fires before the iframe editor is updated with content from the textarea. Return false
25873 * to cancel the push.
25874 * @param {HtmlEditor} this
25875 * @param {String} html
25880 * Fires when the textarea is updated with content from the editor iframe.
25881 * @param {HtmlEditor} this
25882 * @param {String} html
25887 * Fires when the iframe editor is updated with content from the textarea.
25888 * @param {HtmlEditor} this
25889 * @param {String} html
25893 * @event editmodechange
25894 * Fires when the editor switches edit modes
25895 * @param {HtmlEditor} this
25896 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25898 editmodechange: true,
25900 * @event editorevent
25901 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25902 * @param {HtmlEditor} this
25906 * @event firstfocus
25907 * Fires when on first focus - needed by toolbars..
25908 * @param {HtmlEditor} this
25913 * Auto save the htmlEditor value as a file into Events
25914 * @param {HtmlEditor} this
25918 * @event savedpreview
25919 * preview the saved version of htmlEditor
25920 * @param {HtmlEditor} this
25927 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25931 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25936 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25941 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25946 * @cfg {Number} height (in pixels)
25950 * @cfg {Number} width (in pixels)
25955 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25958 stylesheets: false,
25963 // private properties
25964 validationEvent : false,
25966 initialized : false,
25969 onFocus : Roo.emptyFn,
25971 hideMode:'offsets',
25973 tbContainer : false,
25977 toolbarContainer :function() {
25978 return this.wrap.select('.x-html-editor-tb',true).first();
25982 * Protected method that will not generally be called directly. It
25983 * is called when the editor creates its toolbar. Override this method if you need to
25984 * add custom toolbar buttons.
25985 * @param {HtmlEditor} editor
25987 createToolbar : function(){
25988 Roo.log('renewing');
25989 Roo.log("create toolbars");
25991 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25992 this.toolbars[0].render(this.toolbarContainer());
25996 // if (!editor.toolbars || !editor.toolbars.length) {
25997 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26000 // for (var i =0 ; i < editor.toolbars.length;i++) {
26001 // editor.toolbars[i] = Roo.factory(
26002 // typeof(editor.toolbars[i]) == 'string' ?
26003 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26004 // Roo.bootstrap.HtmlEditor);
26005 // editor.toolbars[i].init(editor);
26011 onRender : function(ct, position)
26013 // Roo.log("Call onRender: " + this.xtype);
26015 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26017 this.wrap = this.inputEl().wrap({
26018 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26021 this.editorcore.onRender(ct, position);
26023 if (this.resizable) {
26024 this.resizeEl = new Roo.Resizable(this.wrap, {
26028 minHeight : this.height,
26029 height: this.height,
26030 handles : this.resizable,
26033 resize : function(r, w, h) {
26034 _t.onResize(w,h); // -something
26040 this.createToolbar(this);
26043 if(!this.width && this.resizable){
26044 this.setSize(this.wrap.getSize());
26046 if (this.resizeEl) {
26047 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26048 // should trigger onReize..
26054 onResize : function(w, h)
26056 Roo.log('resize: ' +w + ',' + h );
26057 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26061 if(this.inputEl() ){
26062 if(typeof w == 'number'){
26063 var aw = w - this.wrap.getFrameWidth('lr');
26064 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26067 if(typeof h == 'number'){
26068 var tbh = -11; // fixme it needs to tool bar size!
26069 for (var i =0; i < this.toolbars.length;i++) {
26070 // fixme - ask toolbars for heights?
26071 tbh += this.toolbars[i].el.getHeight();
26072 //if (this.toolbars[i].footer) {
26073 // tbh += this.toolbars[i].footer.el.getHeight();
26081 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26082 ah -= 5; // knock a few pixes off for look..
26083 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26087 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26088 this.editorcore.onResize(ew,eh);
26093 * Toggles the editor between standard and source edit mode.
26094 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26096 toggleSourceEdit : function(sourceEditMode)
26098 this.editorcore.toggleSourceEdit(sourceEditMode);
26100 if(this.editorcore.sourceEditMode){
26101 Roo.log('editor - showing textarea');
26104 // Roo.log(this.syncValue());
26106 this.inputEl().removeClass(['hide', 'x-hidden']);
26107 this.inputEl().dom.removeAttribute('tabIndex');
26108 this.inputEl().focus();
26110 Roo.log('editor - hiding textarea');
26112 // Roo.log(this.pushValue());
26115 this.inputEl().addClass(['hide', 'x-hidden']);
26116 this.inputEl().dom.setAttribute('tabIndex', -1);
26117 //this.deferFocus();
26120 if(this.resizable){
26121 this.setSize(this.wrap.getSize());
26124 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26127 // private (for BoxComponent)
26128 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26130 // private (for BoxComponent)
26131 getResizeEl : function(){
26135 // private (for BoxComponent)
26136 getPositionEl : function(){
26141 initEvents : function(){
26142 this.originalValue = this.getValue();
26146 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26149 // markInvalid : Roo.emptyFn,
26151 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26154 // clearInvalid : Roo.emptyFn,
26156 setValue : function(v){
26157 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26158 this.editorcore.pushValue();
26163 deferFocus : function(){
26164 this.focus.defer(10, this);
26168 focus : function(){
26169 this.editorcore.focus();
26175 onDestroy : function(){
26181 for (var i =0; i < this.toolbars.length;i++) {
26182 // fixme - ask toolbars for heights?
26183 this.toolbars[i].onDestroy();
26186 this.wrap.dom.innerHTML = '';
26187 this.wrap.remove();
26192 onFirstFocus : function(){
26193 //Roo.log("onFirstFocus");
26194 this.editorcore.onFirstFocus();
26195 for (var i =0; i < this.toolbars.length;i++) {
26196 this.toolbars[i].onFirstFocus();
26202 syncValue : function()
26204 this.editorcore.syncValue();
26207 pushValue : function()
26209 this.editorcore.pushValue();
26213 // hide stuff that is not compatible
26227 * @event specialkey
26231 * @cfg {String} fieldClass @hide
26234 * @cfg {String} focusClass @hide
26237 * @cfg {String} autoCreate @hide
26240 * @cfg {String} inputType @hide
26244 * @cfg {String} invalidText @hide
26247 * @cfg {String} msgFx @hide
26250 * @cfg {String} validateOnBlur @hide
26259 Roo.namespace('Roo.bootstrap.htmleditor');
26261 * @class Roo.bootstrap.HtmlEditorToolbar1
26267 new Roo.bootstrap.HtmlEditor({
26270 new Roo.bootstrap.HtmlEditorToolbar1({
26271 disable : { fonts: 1 , format: 1, ..., ... , ...],
26277 * @cfg {Object} disable List of elements to disable..
26278 * @cfg {Array} btns List of additional buttons.
26282 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26285 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26288 Roo.apply(this, config);
26290 // default disabled, based on 'good practice'..
26291 this.disable = this.disable || {};
26292 Roo.applyIf(this.disable, {
26295 specialElements : true
26297 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26299 this.editor = config.editor;
26300 this.editorcore = config.editor.editorcore;
26302 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26304 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26305 // dont call parent... till later.
26307 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26312 editorcore : false,
26317 "h1","h2","h3","h4","h5","h6",
26319 "abbr", "acronym", "address", "cite", "samp", "var",
26323 onRender : function(ct, position)
26325 // Roo.log("Call onRender: " + this.xtype);
26327 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26329 this.el.dom.style.marginBottom = '0';
26331 var editorcore = this.editorcore;
26332 var editor= this.editor;
26335 var btn = function(id,cmd , toggle, handler, html){
26337 var event = toggle ? 'toggle' : 'click';
26342 xns: Roo.bootstrap,
26346 enableToggle:toggle !== false,
26348 pressed : toggle ? false : null,
26351 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26352 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26358 // var cb_box = function...
26363 xns: Roo.bootstrap,
26368 xns: Roo.bootstrap,
26372 Roo.each(this.formats, function(f) {
26373 style.menu.items.push({
26375 xns: Roo.bootstrap,
26376 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26381 editorcore.insertTag(this.tagname);
26388 children.push(style);
26390 btn('bold',false,true);
26391 btn('italic',false,true);
26392 btn('align-left', 'justifyleft',true);
26393 btn('align-center', 'justifycenter',true);
26394 btn('align-right' , 'justifyright',true);
26395 btn('link', false, false, function(btn) {
26396 //Roo.log("create link?");
26397 var url = prompt(this.createLinkText, this.defaultLinkValue);
26398 if(url && url != 'http:/'+'/'){
26399 this.editorcore.relayCmd('createlink', url);
26402 btn('list','insertunorderedlist',true);
26403 btn('pencil', false,true, function(btn){
26405 this.toggleSourceEdit(btn.pressed);
26408 if (this.editor.btns.length > 0) {
26409 for (var i = 0; i<this.editor.btns.length; i++) {
26410 children.push(this.editor.btns[i]);
26418 xns: Roo.bootstrap,
26423 xns: Roo.bootstrap,
26428 cog.menu.items.push({
26430 xns: Roo.bootstrap,
26431 html : Clean styles,
26436 editorcore.insertTag(this.tagname);
26445 this.xtype = 'NavSimplebar';
26447 for(var i=0;i< children.length;i++) {
26449 this.buttons.add(this.addxtypeChild(children[i]));
26453 editor.on('editorevent', this.updateToolbar, this);
26455 onBtnClick : function(id)
26457 this.editorcore.relayCmd(id);
26458 this.editorcore.focus();
26462 * Protected method that will not generally be called directly. It triggers
26463 * a toolbar update by reading the markup state of the current selection in the editor.
26465 updateToolbar: function(){
26467 if(!this.editorcore.activated){
26468 this.editor.onFirstFocus(); // is this neeed?
26472 var btns = this.buttons;
26473 var doc = this.editorcore.doc;
26474 btns.get('bold').setActive(doc.queryCommandState('bold'));
26475 btns.get('italic').setActive(doc.queryCommandState('italic'));
26476 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26478 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26479 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26480 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26482 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26483 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26486 var ans = this.editorcore.getAllAncestors();
26487 if (this.formatCombo) {
26490 var store = this.formatCombo.store;
26491 this.formatCombo.setValue("");
26492 for (var i =0; i < ans.length;i++) {
26493 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26495 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26503 // hides menus... - so this cant be on a menu...
26504 Roo.bootstrap.MenuMgr.hideAll();
26506 Roo.bootstrap.MenuMgr.hideAll();
26507 //this.editorsyncValue();
26509 onFirstFocus: function() {
26510 this.buttons.each(function(item){
26514 toggleSourceEdit : function(sourceEditMode){
26517 if(sourceEditMode){
26518 Roo.log("disabling buttons");
26519 this.buttons.each( function(item){
26520 if(item.cmd != 'pencil'){
26526 Roo.log("enabling buttons");
26527 if(this.editorcore.initialized){
26528 this.buttons.each( function(item){
26534 Roo.log("calling toggole on editor");
26535 // tell the editor that it's been pressed..
26536 this.editor.toggleSourceEdit(sourceEditMode);
26550 * @class Roo.bootstrap.Markdown
26551 * @extends Roo.bootstrap.TextArea
26552 * Bootstrap Showdown editable area
26553 * @cfg {string} content
26556 * Create a new Showdown
26559 Roo.bootstrap.Markdown = function(config){
26560 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26564 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26568 initEvents : function()
26571 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26572 this.markdownEl = this.el.createChild({
26573 cls : 'roo-markdown-area'
26575 this.inputEl().addClass('d-none');
26576 if (this.getValue() == '') {
26577 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26580 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26582 this.markdownEl.on('click', this.toggleTextEdit, this);
26583 this.on('blur', this.toggleTextEdit, this);
26584 this.on('specialkey', this.resizeTextArea, this);
26587 toggleTextEdit : function()
26589 var sh = this.markdownEl.getHeight();
26590 this.inputEl().addClass('d-none');
26591 this.markdownEl.addClass('d-none');
26592 if (!this.editing) {
26594 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26595 this.inputEl().removeClass('d-none');
26596 this.inputEl().focus();
26597 this.editing = true;
26600 // show showdown...
26601 this.updateMarkdown();
26602 this.markdownEl.removeClass('d-none');
26603 this.editing = false;
26606 updateMarkdown : function()
26608 if (this.getValue() == '') {
26609 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26613 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26616 resizeTextArea: function () {
26619 Roo.log([sh, this.getValue().split("\n").length * 30]);
26620 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26622 setValue : function(val)
26624 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26625 if (!this.editing) {
26626 this.updateMarkdown();
26632 if (!this.editing) {
26633 this.toggleTextEdit();
26641 * @class Roo.bootstrap.Table.AbstractSelectionModel
26642 * @extends Roo.util.Observable
26643 * Abstract base class for grid SelectionModels. It provides the interface that should be
26644 * implemented by descendant classes. This class should not be directly instantiated.
26647 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26648 this.locked = false;
26649 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26653 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26654 /** @ignore Called by the grid automatically. Do not call directly. */
26655 init : function(grid){
26661 * Locks the selections.
26664 this.locked = true;
26668 * Unlocks the selections.
26670 unlock : function(){
26671 this.locked = false;
26675 * Returns true if the selections are locked.
26676 * @return {Boolean}
26678 isLocked : function(){
26679 return this.locked;
26683 initEvents : function ()
26689 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26690 * @class Roo.bootstrap.Table.RowSelectionModel
26691 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26692 * It supports multiple selections and keyboard selection/navigation.
26694 * @param {Object} config
26697 Roo.bootstrap.Table.RowSelectionModel = function(config){
26698 Roo.apply(this, config);
26699 this.selections = new Roo.util.MixedCollection(false, function(o){
26704 this.lastActive = false;
26708 * @event selectionchange
26709 * Fires when the selection changes
26710 * @param {SelectionModel} this
26712 "selectionchange" : true,
26714 * @event afterselectionchange
26715 * Fires after the selection changes (eg. by key press or clicking)
26716 * @param {SelectionModel} this
26718 "afterselectionchange" : true,
26720 * @event beforerowselect
26721 * Fires when a row is selected being selected, return false to cancel.
26722 * @param {SelectionModel} this
26723 * @param {Number} rowIndex The selected index
26724 * @param {Boolean} keepExisting False if other selections will be cleared
26726 "beforerowselect" : true,
26729 * Fires when a row is selected.
26730 * @param {SelectionModel} this
26731 * @param {Number} rowIndex The selected index
26732 * @param {Roo.data.Record} r The record
26734 "rowselect" : true,
26736 * @event rowdeselect
26737 * Fires when a row is deselected.
26738 * @param {SelectionModel} this
26739 * @param {Number} rowIndex The selected index
26741 "rowdeselect" : true
26743 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26744 this.locked = false;
26747 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26749 * @cfg {Boolean} singleSelect
26750 * True to allow selection of only one row at a time (defaults to false)
26752 singleSelect : false,
26755 initEvents : function()
26758 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26759 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26760 //}else{ // allow click to work like normal
26761 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26763 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26764 this.grid.on("rowclick", this.handleMouseDown, this);
26766 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26767 "up" : function(e){
26769 this.selectPrevious(e.shiftKey);
26770 }else if(this.last !== false && this.lastActive !== false){
26771 var last = this.last;
26772 this.selectRange(this.last, this.lastActive-1);
26773 this.grid.getView().focusRow(this.lastActive);
26774 if(last !== false){
26778 this.selectFirstRow();
26780 this.fireEvent("afterselectionchange", this);
26782 "down" : function(e){
26784 this.selectNext(e.shiftKey);
26785 }else if(this.last !== false && this.lastActive !== false){
26786 var last = this.last;
26787 this.selectRange(this.last, this.lastActive+1);
26788 this.grid.getView().focusRow(this.lastActive);
26789 if(last !== false){
26793 this.selectFirstRow();
26795 this.fireEvent("afterselectionchange", this);
26799 this.grid.store.on('load', function(){
26800 this.selections.clear();
26803 var view = this.grid.view;
26804 view.on("refresh", this.onRefresh, this);
26805 view.on("rowupdated", this.onRowUpdated, this);
26806 view.on("rowremoved", this.onRemove, this);
26811 onRefresh : function()
26813 var ds = this.grid.store, i, v = this.grid.view;
26814 var s = this.selections;
26815 s.each(function(r){
26816 if((i = ds.indexOfId(r.id)) != -1){
26825 onRemove : function(v, index, r){
26826 this.selections.remove(r);
26830 onRowUpdated : function(v, index, r){
26831 if(this.isSelected(r)){
26832 v.onRowSelect(index);
26838 * @param {Array} records The records to select
26839 * @param {Boolean} keepExisting (optional) True to keep existing selections
26841 selectRecords : function(records, keepExisting)
26844 this.clearSelections();
26846 var ds = this.grid.store;
26847 for(var i = 0, len = records.length; i < len; i++){
26848 this.selectRow(ds.indexOf(records[i]), true);
26853 * Gets the number of selected rows.
26856 getCount : function(){
26857 return this.selections.length;
26861 * Selects the first row in the grid.
26863 selectFirstRow : function(){
26868 * Select the last row.
26869 * @param {Boolean} keepExisting (optional) True to keep existing selections
26871 selectLastRow : function(keepExisting){
26872 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26873 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26877 * Selects the row immediately following the last selected row.
26878 * @param {Boolean} keepExisting (optional) True to keep existing selections
26880 selectNext : function(keepExisting)
26882 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26883 this.selectRow(this.last+1, keepExisting);
26884 this.grid.getView().focusRow(this.last);
26889 * Selects the row that precedes the last selected row.
26890 * @param {Boolean} keepExisting (optional) True to keep existing selections
26892 selectPrevious : function(keepExisting){
26894 this.selectRow(this.last-1, keepExisting);
26895 this.grid.getView().focusRow(this.last);
26900 * Returns the selected records
26901 * @return {Array} Array of selected records
26903 getSelections : function(){
26904 return [].concat(this.selections.items);
26908 * Returns the first selected record.
26911 getSelected : function(){
26912 return this.selections.itemAt(0);
26917 * Clears all selections.
26919 clearSelections : function(fast)
26925 var ds = this.grid.store;
26926 var s = this.selections;
26927 s.each(function(r){
26928 this.deselectRow(ds.indexOfId(r.id));
26932 this.selections.clear();
26939 * Selects all rows.
26941 selectAll : function(){
26945 this.selections.clear();
26946 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26947 this.selectRow(i, true);
26952 * Returns True if there is a selection.
26953 * @return {Boolean}
26955 hasSelection : function(){
26956 return this.selections.length > 0;
26960 * Returns True if the specified row is selected.
26961 * @param {Number/Record} record The record or index of the record to check
26962 * @return {Boolean}
26964 isSelected : function(index){
26965 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26966 return (r && this.selections.key(r.id) ? true : false);
26970 * Returns True if the specified record id is selected.
26971 * @param {String} id The id of record to check
26972 * @return {Boolean}
26974 isIdSelected : function(id){
26975 return (this.selections.key(id) ? true : false);
26980 handleMouseDBClick : function(e, t){
26984 handleMouseDown : function(e, t)
26986 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26987 if(this.isLocked() || rowIndex < 0 ){
26990 if(e.shiftKey && this.last !== false){
26991 var last = this.last;
26992 this.selectRange(last, rowIndex, e.ctrlKey);
26993 this.last = last; // reset the last
26997 var isSelected = this.isSelected(rowIndex);
26998 //Roo.log("select row:" + rowIndex);
27000 this.deselectRow(rowIndex);
27002 this.selectRow(rowIndex, true);
27006 if(e.button !== 0 && isSelected){
27007 alert('rowIndex 2: ' + rowIndex);
27008 view.focusRow(rowIndex);
27009 }else if(e.ctrlKey && isSelected){
27010 this.deselectRow(rowIndex);
27011 }else if(!isSelected){
27012 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27013 view.focusRow(rowIndex);
27017 this.fireEvent("afterselectionchange", this);
27020 handleDragableRowClick : function(grid, rowIndex, e)
27022 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27023 this.selectRow(rowIndex, false);
27024 grid.view.focusRow(rowIndex);
27025 this.fireEvent("afterselectionchange", this);
27030 * Selects multiple rows.
27031 * @param {Array} rows Array of the indexes of the row to select
27032 * @param {Boolean} keepExisting (optional) True to keep existing selections
27034 selectRows : function(rows, keepExisting){
27036 this.clearSelections();
27038 for(var i = 0, len = rows.length; i < len; i++){
27039 this.selectRow(rows[i], true);
27044 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27045 * @param {Number} startRow The index of the first row in the range
27046 * @param {Number} endRow The index of the last row in the range
27047 * @param {Boolean} keepExisting (optional) True to retain existing selections
27049 selectRange : function(startRow, endRow, keepExisting){
27054 this.clearSelections();
27056 if(startRow <= endRow){
27057 for(var i = startRow; i <= endRow; i++){
27058 this.selectRow(i, true);
27061 for(var i = startRow; i >= endRow; i--){
27062 this.selectRow(i, true);
27068 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27069 * @param {Number} startRow The index of the first row in the range
27070 * @param {Number} endRow The index of the last row in the range
27072 deselectRange : function(startRow, endRow, preventViewNotify){
27076 for(var i = startRow; i <= endRow; i++){
27077 this.deselectRow(i, preventViewNotify);
27083 * @param {Number} row The index of the row to select
27084 * @param {Boolean} keepExisting (optional) True to keep existing selections
27086 selectRow : function(index, keepExisting, preventViewNotify)
27088 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27091 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27092 if(!keepExisting || this.singleSelect){
27093 this.clearSelections();
27096 var r = this.grid.store.getAt(index);
27097 //console.log('selectRow - record id :' + r.id);
27099 this.selections.add(r);
27100 this.last = this.lastActive = index;
27101 if(!preventViewNotify){
27102 var proxy = new Roo.Element(
27103 this.grid.getRowDom(index)
27105 proxy.addClass('bg-info info');
27107 this.fireEvent("rowselect", this, index, r);
27108 this.fireEvent("selectionchange", this);
27114 * @param {Number} row The index of the row to deselect
27116 deselectRow : function(index, preventViewNotify)
27121 if(this.last == index){
27124 if(this.lastActive == index){
27125 this.lastActive = false;
27128 var r = this.grid.store.getAt(index);
27133 this.selections.remove(r);
27134 //.console.log('deselectRow - record id :' + r.id);
27135 if(!preventViewNotify){
27137 var proxy = new Roo.Element(
27138 this.grid.getRowDom(index)
27140 proxy.removeClass('bg-info info');
27142 this.fireEvent("rowdeselect", this, index);
27143 this.fireEvent("selectionchange", this);
27147 restoreLast : function(){
27149 this.last = this._last;
27154 acceptsNav : function(row, col, cm){
27155 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27159 onEditorKey : function(field, e){
27160 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27165 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27167 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27169 }else if(k == e.ENTER && !e.ctrlKey){
27173 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27175 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27177 }else if(k == e.ESC){
27181 g.startEditing(newCell[0], newCell[1]);
27187 * Ext JS Library 1.1.1
27188 * Copyright(c) 2006-2007, Ext JS, LLC.
27190 * Originally Released Under LGPL - original licence link has changed is not relivant.
27193 * <script type="text/javascript">
27197 * @class Roo.bootstrap.PagingToolbar
27198 * @extends Roo.bootstrap.NavSimplebar
27199 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27201 * Create a new PagingToolbar
27202 * @param {Object} config The config object
27203 * @param {Roo.data.Store} store
27205 Roo.bootstrap.PagingToolbar = function(config)
27207 // old args format still supported... - xtype is prefered..
27208 // created from xtype...
27210 this.ds = config.dataSource;
27212 if (config.store && !this.ds) {
27213 this.store= Roo.factory(config.store, Roo.data);
27214 this.ds = this.store;
27215 this.ds.xmodule = this.xmodule || false;
27218 this.toolbarItems = [];
27219 if (config.items) {
27220 this.toolbarItems = config.items;
27223 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27228 this.bind(this.ds);
27231 if (Roo.bootstrap.version == 4) {
27232 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27234 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27239 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27241 * @cfg {Roo.data.Store} dataSource
27242 * The underlying data store providing the paged data
27245 * @cfg {String/HTMLElement/Element} container
27246 * container The id or element that will contain the toolbar
27249 * @cfg {Boolean} displayInfo
27250 * True to display the displayMsg (defaults to false)
27253 * @cfg {Number} pageSize
27254 * The number of records to display per page (defaults to 20)
27258 * @cfg {String} displayMsg
27259 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27261 displayMsg : 'Displaying {0} - {1} of {2}',
27263 * @cfg {String} emptyMsg
27264 * The message to display when no records are found (defaults to "No data to display")
27266 emptyMsg : 'No data to display',
27268 * Customizable piece of the default paging text (defaults to "Page")
27271 beforePageText : "Page",
27273 * Customizable piece of the default paging text (defaults to "of %0")
27276 afterPageText : "of {0}",
27278 * Customizable piece of the default paging text (defaults to "First Page")
27281 firstText : "First Page",
27283 * Customizable piece of the default paging text (defaults to "Previous Page")
27286 prevText : "Previous Page",
27288 * Customizable piece of the default paging text (defaults to "Next Page")
27291 nextText : "Next Page",
27293 * Customizable piece of the default paging text (defaults to "Last Page")
27296 lastText : "Last Page",
27298 * Customizable piece of the default paging text (defaults to "Refresh")
27301 refreshText : "Refresh",
27305 onRender : function(ct, position)
27307 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27308 this.navgroup.parentId = this.id;
27309 this.navgroup.onRender(this.el, null);
27310 // add the buttons to the navgroup
27312 if(this.displayInfo){
27313 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27314 this.displayEl = this.el.select('.x-paging-info', true).first();
27315 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27316 // this.displayEl = navel.el.select('span',true).first();
27322 Roo.each(_this.buttons, function(e){ // this might need to use render????
27323 Roo.factory(e).render(_this.el);
27327 Roo.each(_this.toolbarItems, function(e) {
27328 _this.navgroup.addItem(e);
27332 this.first = this.navgroup.addItem({
27333 tooltip: this.firstText,
27334 cls: "prev btn-outline-secondary",
27335 html : ' <i class="fa fa-step-backward"></i>',
27337 preventDefault: true,
27338 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27341 this.prev = this.navgroup.addItem({
27342 tooltip: this.prevText,
27343 cls: "prev btn-outline-secondary",
27344 html : ' <i class="fa fa-backward"></i>',
27346 preventDefault: true,
27347 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27349 //this.addSeparator();
27352 var field = this.navgroup.addItem( {
27354 cls : 'x-paging-position btn-outline-secondary',
27356 html : this.beforePageText +
27357 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27358 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27361 this.field = field.el.select('input', true).first();
27362 this.field.on("keydown", this.onPagingKeydown, this);
27363 this.field.on("focus", function(){this.dom.select();});
27366 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27367 //this.field.setHeight(18);
27368 //this.addSeparator();
27369 this.next = this.navgroup.addItem({
27370 tooltip: this.nextText,
27371 cls: "next btn-outline-secondary",
27372 html : ' <i class="fa fa-forward"></i>',
27374 preventDefault: true,
27375 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27377 this.last = this.navgroup.addItem({
27378 tooltip: this.lastText,
27379 html : ' <i class="fa fa-step-forward"></i>',
27380 cls: "next btn-outline-secondary",
27382 preventDefault: true,
27383 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27385 //this.addSeparator();
27386 this.loading = this.navgroup.addItem({
27387 tooltip: this.refreshText,
27388 cls: "btn-outline-secondary",
27389 html : ' <i class="fa fa-refresh"></i>',
27390 preventDefault: true,
27391 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27397 updateInfo : function(){
27398 if(this.displayEl){
27399 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27400 var msg = count == 0 ?
27404 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27406 this.displayEl.update(msg);
27411 onLoad : function(ds, r, o)
27413 this.cursor = o.params && o.params.start ? o.params.start : 0;
27415 var d = this.getPageData(),
27420 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27421 this.field.dom.value = ap;
27422 this.first.setDisabled(ap == 1);
27423 this.prev.setDisabled(ap == 1);
27424 this.next.setDisabled(ap == ps);
27425 this.last.setDisabled(ap == ps);
27426 this.loading.enable();
27431 getPageData : function(){
27432 var total = this.ds.getTotalCount();
27435 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27436 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27441 onLoadError : function(){
27442 this.loading.enable();
27446 onPagingKeydown : function(e){
27447 var k = e.getKey();
27448 var d = this.getPageData();
27450 var v = this.field.dom.value, pageNum;
27451 if(!v || isNaN(pageNum = parseInt(v, 10))){
27452 this.field.dom.value = d.activePage;
27455 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27456 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27459 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))
27461 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27462 this.field.dom.value = pageNum;
27463 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27466 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27468 var v = this.field.dom.value, pageNum;
27469 var increment = (e.shiftKey) ? 10 : 1;
27470 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27473 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27474 this.field.dom.value = d.activePage;
27477 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27479 this.field.dom.value = parseInt(v, 10) + increment;
27480 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27481 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27488 beforeLoad : function(){
27490 this.loading.disable();
27495 onClick : function(which){
27504 ds.load({params:{start: 0, limit: this.pageSize}});
27507 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27510 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27513 var total = ds.getTotalCount();
27514 var extra = total % this.pageSize;
27515 var lastStart = extra ? (total - extra) : total-this.pageSize;
27516 ds.load({params:{start: lastStart, limit: this.pageSize}});
27519 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27525 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27526 * @param {Roo.data.Store} store The data store to unbind
27528 unbind : function(ds){
27529 ds.un("beforeload", this.beforeLoad, this);
27530 ds.un("load", this.onLoad, this);
27531 ds.un("loadexception", this.onLoadError, this);
27532 ds.un("remove", this.updateInfo, this);
27533 ds.un("add", this.updateInfo, this);
27534 this.ds = undefined;
27538 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27539 * @param {Roo.data.Store} store The data store to bind
27541 bind : function(ds){
27542 ds.on("beforeload", this.beforeLoad, this);
27543 ds.on("load", this.onLoad, this);
27544 ds.on("loadexception", this.onLoadError, this);
27545 ds.on("remove", this.updateInfo, this);
27546 ds.on("add", this.updateInfo, this);
27557 * @class Roo.bootstrap.MessageBar
27558 * @extends Roo.bootstrap.Component
27559 * Bootstrap MessageBar class
27560 * @cfg {String} html contents of the MessageBar
27561 * @cfg {String} weight (info | success | warning | danger) default info
27562 * @cfg {String} beforeClass insert the bar before the given class
27563 * @cfg {Boolean} closable (true | false) default false
27564 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27567 * Create a new Element
27568 * @param {Object} config The config object
27571 Roo.bootstrap.MessageBar = function(config){
27572 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27575 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27581 beforeClass: 'bootstrap-sticky-wrap',
27583 getAutoCreate : function(){
27587 cls: 'alert alert-dismissable alert-' + this.weight,
27592 html: this.html || ''
27598 cfg.cls += ' alert-messages-fixed';
27612 onRender : function(ct, position)
27614 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27617 var cfg = Roo.apply({}, this.getAutoCreate());
27621 cfg.cls += ' ' + this.cls;
27624 cfg.style = this.style;
27626 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27628 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27631 this.el.select('>button.close').on('click', this.hide, this);
27637 if (!this.rendered) {
27643 this.fireEvent('show', this);
27649 if (!this.rendered) {
27655 this.fireEvent('hide', this);
27658 update : function()
27660 // var e = this.el.dom.firstChild;
27662 // if(this.closable){
27663 // e = e.nextSibling;
27666 // e.data = this.html || '';
27668 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27684 * @class Roo.bootstrap.Graph
27685 * @extends Roo.bootstrap.Component
27686 * Bootstrap Graph class
27690 @cfg {String} graphtype bar | vbar | pie
27691 @cfg {number} g_x coodinator | centre x (pie)
27692 @cfg {number} g_y coodinator | centre y (pie)
27693 @cfg {number} g_r radius (pie)
27694 @cfg {number} g_height height of the chart (respected by all elements in the set)
27695 @cfg {number} g_width width of the chart (respected by all elements in the set)
27696 @cfg {Object} title The title of the chart
27699 -opts (object) options for the chart
27701 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27702 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27704 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.
27705 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27707 o stretch (boolean)
27709 -opts (object) options for the pie
27712 o startAngle (number)
27713 o endAngle (number)
27717 * Create a new Input
27718 * @param {Object} config The config object
27721 Roo.bootstrap.Graph = function(config){
27722 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27728 * The img click event for the img.
27729 * @param {Roo.EventObject} e
27735 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27746 //g_colors: this.colors,
27753 getAutoCreate : function(){
27764 onRender : function(ct,position){
27767 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27769 if (typeof(Raphael) == 'undefined') {
27770 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27774 this.raphael = Raphael(this.el.dom);
27776 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27777 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27778 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27779 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27781 r.text(160, 10, "Single Series Chart").attr(txtattr);
27782 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27783 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27784 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27786 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27787 r.barchart(330, 10, 300, 220, data1);
27788 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27789 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27792 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27793 // r.barchart(30, 30, 560, 250, xdata, {
27794 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27795 // axis : "0 0 1 1",
27796 // axisxlabels : xdata
27797 // //yvalues : cols,
27800 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27802 // this.load(null,xdata,{
27803 // axis : "0 0 1 1",
27804 // axisxlabels : xdata
27809 load : function(graphtype,xdata,opts)
27811 this.raphael.clear();
27813 graphtype = this.graphtype;
27818 var r = this.raphael,
27819 fin = function () {
27820 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27822 fout = function () {
27823 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27825 pfin = function() {
27826 this.sector.stop();
27827 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27830 this.label[0].stop();
27831 this.label[0].attr({ r: 7.5 });
27832 this.label[1].attr({ "font-weight": 800 });
27835 pfout = function() {
27836 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27839 this.label[0].animate({ r: 5 }, 500, "bounce");
27840 this.label[1].attr({ "font-weight": 400 });
27846 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27849 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27852 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27853 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27855 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27862 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27867 setTitle: function(o)
27872 initEvents: function() {
27875 this.el.on('click', this.onClick, this);
27879 onClick : function(e)
27881 Roo.log('img onclick');
27882 this.fireEvent('click', this, e);
27894 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27897 * @class Roo.bootstrap.dash.NumberBox
27898 * @extends Roo.bootstrap.Component
27899 * Bootstrap NumberBox class
27900 * @cfg {String} headline Box headline
27901 * @cfg {String} content Box content
27902 * @cfg {String} icon Box icon
27903 * @cfg {String} footer Footer text
27904 * @cfg {String} fhref Footer href
27907 * Create a new NumberBox
27908 * @param {Object} config The config object
27912 Roo.bootstrap.dash.NumberBox = function(config){
27913 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27917 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27926 getAutoCreate : function(){
27930 cls : 'small-box ',
27938 cls : 'roo-headline',
27939 html : this.headline
27943 cls : 'roo-content',
27944 html : this.content
27958 cls : 'ion ' + this.icon
27967 cls : 'small-box-footer',
27968 href : this.fhref || '#',
27972 cfg.cn.push(footer);
27979 onRender : function(ct,position){
27980 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27987 setHeadline: function (value)
27989 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27992 setFooter: function (value, href)
27994 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27997 this.el.select('a.small-box-footer',true).first().attr('href', href);
28002 setContent: function (value)
28004 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28007 initEvents: function()
28021 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28024 * @class Roo.bootstrap.dash.TabBox
28025 * @extends Roo.bootstrap.Component
28026 * Bootstrap TabBox class
28027 * @cfg {String} title Title of the TabBox
28028 * @cfg {String} icon Icon of the TabBox
28029 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28030 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28033 * Create a new TabBox
28034 * @param {Object} config The config object
28038 Roo.bootstrap.dash.TabBox = function(config){
28039 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28044 * When a pane is added
28045 * @param {Roo.bootstrap.dash.TabPane} pane
28049 * @event activatepane
28050 * When a pane is activated
28051 * @param {Roo.bootstrap.dash.TabPane} pane
28053 "activatepane" : true
28061 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28066 tabScrollable : false,
28068 getChildContainer : function()
28070 return this.el.select('.tab-content', true).first();
28073 getAutoCreate : function(){
28077 cls: 'pull-left header',
28085 cls: 'fa ' + this.icon
28091 cls: 'nav nav-tabs pull-right',
28097 if(this.tabScrollable){
28104 cls: 'nav nav-tabs pull-right',
28115 cls: 'nav-tabs-custom',
28120 cls: 'tab-content no-padding',
28128 initEvents : function()
28130 //Roo.log('add add pane handler');
28131 this.on('addpane', this.onAddPane, this);
28134 * Updates the box title
28135 * @param {String} html to set the title to.
28137 setTitle : function(value)
28139 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28141 onAddPane : function(pane)
28143 this.panes.push(pane);
28144 //Roo.log('addpane');
28146 // tabs are rendere left to right..
28147 if(!this.showtabs){
28151 var ctr = this.el.select('.nav-tabs', true).first();
28154 var existing = ctr.select('.nav-tab',true);
28155 var qty = existing.getCount();;
28158 var tab = ctr.createChild({
28160 cls : 'nav-tab' + (qty ? '' : ' active'),
28168 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28171 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28173 pane.el.addClass('active');
28178 onTabClick : function(ev,un,ob,pane)
28180 //Roo.log('tab - prev default');
28181 ev.preventDefault();
28184 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28185 pane.tab.addClass('active');
28186 //Roo.log(pane.title);
28187 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28188 // technically we should have a deactivate event.. but maybe add later.
28189 // and it should not de-activate the selected tab...
28190 this.fireEvent('activatepane', pane);
28191 pane.el.addClass('active');
28192 pane.fireEvent('activate');
28197 getActivePane : function()
28200 Roo.each(this.panes, function(p) {
28201 if(p.el.hasClass('active')){
28222 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28224 * @class Roo.bootstrap.TabPane
28225 * @extends Roo.bootstrap.Component
28226 * Bootstrap TabPane class
28227 * @cfg {Boolean} active (false | true) Default false
28228 * @cfg {String} title title of panel
28232 * Create a new TabPane
28233 * @param {Object} config The config object
28236 Roo.bootstrap.dash.TabPane = function(config){
28237 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28243 * When a pane is activated
28244 * @param {Roo.bootstrap.dash.TabPane} pane
28251 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28256 // the tabBox that this is attached to.
28259 getAutoCreate : function()
28267 cfg.cls += ' active';
28272 initEvents : function()
28274 //Roo.log('trigger add pane handler');
28275 this.parent().fireEvent('addpane', this)
28279 * Updates the tab title
28280 * @param {String} html to set the title to.
28282 setTitle: function(str)
28288 this.tab.select('a', true).first().dom.innerHTML = str;
28305 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28308 * @class Roo.bootstrap.menu.Menu
28309 * @extends Roo.bootstrap.Component
28310 * Bootstrap Menu class - container for Menu
28311 * @cfg {String} html Text of the menu
28312 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28313 * @cfg {String} icon Font awesome icon
28314 * @cfg {String} pos Menu align to (top | bottom) default bottom
28318 * Create a new Menu
28319 * @param {Object} config The config object
28323 Roo.bootstrap.menu.Menu = function(config){
28324 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28328 * @event beforeshow
28329 * Fires before this menu is displayed
28330 * @param {Roo.bootstrap.menu.Menu} this
28334 * @event beforehide
28335 * Fires before this menu is hidden
28336 * @param {Roo.bootstrap.menu.Menu} this
28341 * Fires after this menu is displayed
28342 * @param {Roo.bootstrap.menu.Menu} this
28347 * Fires after this menu is hidden
28348 * @param {Roo.bootstrap.menu.Menu} this
28353 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28354 * @param {Roo.bootstrap.menu.Menu} this
28355 * @param {Roo.EventObject} e
28362 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28366 weight : 'default',
28371 getChildContainer : function() {
28372 if(this.isSubMenu){
28376 return this.el.select('ul.dropdown-menu', true).first();
28379 getAutoCreate : function()
28384 cls : 'roo-menu-text',
28392 cls : 'fa ' + this.icon
28403 cls : 'dropdown-button btn btn-' + this.weight,
28408 cls : 'dropdown-toggle btn btn-' + this.weight,
28418 cls : 'dropdown-menu'
28424 if(this.pos == 'top'){
28425 cfg.cls += ' dropup';
28428 if(this.isSubMenu){
28431 cls : 'dropdown-menu'
28438 onRender : function(ct, position)
28440 this.isSubMenu = ct.hasClass('dropdown-submenu');
28442 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28445 initEvents : function()
28447 if(this.isSubMenu){
28451 this.hidden = true;
28453 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28454 this.triggerEl.on('click', this.onTriggerPress, this);
28456 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28457 this.buttonEl.on('click', this.onClick, this);
28463 if(this.isSubMenu){
28467 return this.el.select('ul.dropdown-menu', true).first();
28470 onClick : function(e)
28472 this.fireEvent("click", this, e);
28475 onTriggerPress : function(e)
28477 if (this.isVisible()) {
28484 isVisible : function(){
28485 return !this.hidden;
28490 this.fireEvent("beforeshow", this);
28492 this.hidden = false;
28493 this.el.addClass('open');
28495 Roo.get(document).on("mouseup", this.onMouseUp, this);
28497 this.fireEvent("show", this);
28504 this.fireEvent("beforehide", this);
28506 this.hidden = true;
28507 this.el.removeClass('open');
28509 Roo.get(document).un("mouseup", this.onMouseUp);
28511 this.fireEvent("hide", this);
28514 onMouseUp : function()
28528 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28531 * @class Roo.bootstrap.menu.Item
28532 * @extends Roo.bootstrap.Component
28533 * Bootstrap MenuItem class
28534 * @cfg {Boolean} submenu (true | false) default false
28535 * @cfg {String} html text of the item
28536 * @cfg {String} href the link
28537 * @cfg {Boolean} disable (true | false) default false
28538 * @cfg {Boolean} preventDefault (true | false) default true
28539 * @cfg {String} icon Font awesome icon
28540 * @cfg {String} pos Submenu align to (left | right) default right
28544 * Create a new Item
28545 * @param {Object} config The config object
28549 Roo.bootstrap.menu.Item = function(config){
28550 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28554 * Fires when the mouse is hovering over this menu
28555 * @param {Roo.bootstrap.menu.Item} this
28556 * @param {Roo.EventObject} e
28561 * Fires when the mouse exits this menu
28562 * @param {Roo.bootstrap.menu.Item} this
28563 * @param {Roo.EventObject} e
28569 * The raw click event for the entire grid.
28570 * @param {Roo.EventObject} e
28576 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28581 preventDefault: true,
28586 getAutoCreate : function()
28591 cls : 'roo-menu-item-text',
28599 cls : 'fa ' + this.icon
28608 href : this.href || '#',
28615 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28619 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28621 if(this.pos == 'left'){
28622 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28629 initEvents : function()
28631 this.el.on('mouseover', this.onMouseOver, this);
28632 this.el.on('mouseout', this.onMouseOut, this);
28634 this.el.select('a', true).first().on('click', this.onClick, this);
28638 onClick : function(e)
28640 if(this.preventDefault){
28641 e.preventDefault();
28644 this.fireEvent("click", this, e);
28647 onMouseOver : function(e)
28649 if(this.submenu && this.pos == 'left'){
28650 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28653 this.fireEvent("mouseover", this, e);
28656 onMouseOut : function(e)
28658 this.fireEvent("mouseout", this, e);
28670 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28673 * @class Roo.bootstrap.menu.Separator
28674 * @extends Roo.bootstrap.Component
28675 * Bootstrap Separator class
28678 * Create a new Separator
28679 * @param {Object} config The config object
28683 Roo.bootstrap.menu.Separator = function(config){
28684 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28687 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28689 getAutoCreate : function(){
28710 * @class Roo.bootstrap.Tooltip
28711 * Bootstrap Tooltip class
28712 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28713 * to determine which dom element triggers the tooltip.
28715 * It needs to add support for additional attributes like tooltip-position
28718 * Create a new Toolti
28719 * @param {Object} config The config object
28722 Roo.bootstrap.Tooltip = function(config){
28723 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28725 this.alignment = Roo.bootstrap.Tooltip.alignment;
28727 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28728 this.alignment = config.alignment;
28733 Roo.apply(Roo.bootstrap.Tooltip, {
28735 * @function init initialize tooltip monitoring.
28739 currentTip : false,
28740 currentRegion : false,
28746 Roo.get(document).on('mouseover', this.enter ,this);
28747 Roo.get(document).on('mouseout', this.leave, this);
28750 this.currentTip = new Roo.bootstrap.Tooltip();
28753 enter : function(ev)
28755 var dom = ev.getTarget();
28757 //Roo.log(['enter',dom]);
28758 var el = Roo.fly(dom);
28759 if (this.currentEl) {
28761 //Roo.log(this.currentEl);
28762 //Roo.log(this.currentEl.contains(dom));
28763 if (this.currentEl == el) {
28766 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28772 if (this.currentTip.el) {
28773 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28777 if(!el || el.dom == document){
28783 // you can not look for children, as if el is the body.. then everythign is the child..
28784 if (!el.attr('tooltip')) { //
28785 if (!el.select("[tooltip]").elements.length) {
28788 // is the mouse over this child...?
28789 bindEl = el.select("[tooltip]").first();
28790 var xy = ev.getXY();
28791 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28792 //Roo.log("not in region.");
28795 //Roo.log("child element over..");
28798 this.currentEl = bindEl;
28799 this.currentTip.bind(bindEl);
28800 this.currentRegion = Roo.lib.Region.getRegion(dom);
28801 this.currentTip.enter();
28804 leave : function(ev)
28806 var dom = ev.getTarget();
28807 //Roo.log(['leave',dom]);
28808 if (!this.currentEl) {
28813 if (dom != this.currentEl.dom) {
28816 var xy = ev.getXY();
28817 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28820 // only activate leave if mouse cursor is outside... bounding box..
28825 if (this.currentTip) {
28826 this.currentTip.leave();
28828 //Roo.log('clear currentEl');
28829 this.currentEl = false;
28834 'left' : ['r-l', [-2,0], 'right'],
28835 'right' : ['l-r', [2,0], 'left'],
28836 'bottom' : ['t-b', [0,2], 'top'],
28837 'top' : [ 'b-t', [0,-2], 'bottom']
28843 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28848 delay : null, // can be { show : 300 , hide: 500}
28852 hoverState : null, //???
28854 placement : 'bottom',
28858 getAutoCreate : function(){
28865 cls : 'tooltip-arrow arrow'
28868 cls : 'tooltip-inner'
28875 bind : function(el)
28880 initEvents : function()
28882 this.arrowEl = this.el.select('.arrow', true).first();
28883 this.innerEl = this.el.select('.tooltip-inner', true).first();
28886 enter : function () {
28888 if (this.timeout != null) {
28889 clearTimeout(this.timeout);
28892 this.hoverState = 'in';
28893 //Roo.log("enter - show");
28894 if (!this.delay || !this.delay.show) {
28899 this.timeout = setTimeout(function () {
28900 if (_t.hoverState == 'in') {
28903 }, this.delay.show);
28907 clearTimeout(this.timeout);
28909 this.hoverState = 'out';
28910 if (!this.delay || !this.delay.hide) {
28916 this.timeout = setTimeout(function () {
28917 //Roo.log("leave - timeout");
28919 if (_t.hoverState == 'out') {
28921 Roo.bootstrap.Tooltip.currentEl = false;
28926 show : function (msg)
28929 this.render(document.body);
28932 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28934 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28936 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28938 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28939 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28941 var placement = typeof this.placement == 'function' ?
28942 this.placement.call(this, this.el, on_el) :
28945 var autoToken = /\s?auto?\s?/i;
28946 var autoPlace = autoToken.test(placement);
28948 placement = placement.replace(autoToken, '') || 'top';
28952 //this.el.setXY([0,0]);
28954 //this.el.dom.style.display='block';
28956 //this.el.appendTo(on_el);
28958 var p = this.getPosition();
28959 var box = this.el.getBox();
28965 var align = this.alignment[placement];
28967 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28969 if(placement == 'top' || placement == 'bottom'){
28971 placement = 'right';
28974 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28975 placement = 'left';
28978 var scroll = Roo.select('body', true).first().getScroll();
28980 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28984 align = this.alignment[placement];
28986 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28990 this.el.alignTo(this.bindEl, align[0],align[1]);
28991 //var arrow = this.el.select('.arrow',true).first();
28992 //arrow.set(align[2],
28994 this.el.addClass(placement);
28995 this.el.addClass("bs-tooltip-"+ placement);
28997 this.el.addClass('in fade show');
28999 this.hoverState = null;
29001 if (this.el.hasClass('fade')) {
29016 //this.el.setXY([0,0]);
29017 this.el.removeClass(['show', 'in']);
29033 * @class Roo.bootstrap.LocationPicker
29034 * @extends Roo.bootstrap.Component
29035 * Bootstrap LocationPicker class
29036 * @cfg {Number} latitude Position when init default 0
29037 * @cfg {Number} longitude Position when init default 0
29038 * @cfg {Number} zoom default 15
29039 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29040 * @cfg {Boolean} mapTypeControl default false
29041 * @cfg {Boolean} disableDoubleClickZoom default false
29042 * @cfg {Boolean} scrollwheel default true
29043 * @cfg {Boolean} streetViewControl default false
29044 * @cfg {Number} radius default 0
29045 * @cfg {String} locationName
29046 * @cfg {Boolean} draggable default true
29047 * @cfg {Boolean} enableAutocomplete default false
29048 * @cfg {Boolean} enableReverseGeocode default true
29049 * @cfg {String} markerTitle
29052 * Create a new LocationPicker
29053 * @param {Object} config The config object
29057 Roo.bootstrap.LocationPicker = function(config){
29059 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29064 * Fires when the picker initialized.
29065 * @param {Roo.bootstrap.LocationPicker} this
29066 * @param {Google Location} location
29070 * @event positionchanged
29071 * Fires when the picker position changed.
29072 * @param {Roo.bootstrap.LocationPicker} this
29073 * @param {Google Location} location
29075 positionchanged : true,
29078 * Fires when the map resize.
29079 * @param {Roo.bootstrap.LocationPicker} this
29084 * Fires when the map show.
29085 * @param {Roo.bootstrap.LocationPicker} this
29090 * Fires when the map hide.
29091 * @param {Roo.bootstrap.LocationPicker} this
29096 * Fires when click the map.
29097 * @param {Roo.bootstrap.LocationPicker} this
29098 * @param {Map event} e
29102 * @event mapRightClick
29103 * Fires when right click the map.
29104 * @param {Roo.bootstrap.LocationPicker} this
29105 * @param {Map event} e
29107 mapRightClick : true,
29109 * @event markerClick
29110 * Fires when click the marker.
29111 * @param {Roo.bootstrap.LocationPicker} this
29112 * @param {Map event} e
29114 markerClick : true,
29116 * @event markerRightClick
29117 * Fires when right click the marker.
29118 * @param {Roo.bootstrap.LocationPicker} this
29119 * @param {Map event} e
29121 markerRightClick : true,
29123 * @event OverlayViewDraw
29124 * Fires when OverlayView Draw
29125 * @param {Roo.bootstrap.LocationPicker} this
29127 OverlayViewDraw : true,
29129 * @event OverlayViewOnAdd
29130 * Fires when OverlayView Draw
29131 * @param {Roo.bootstrap.LocationPicker} this
29133 OverlayViewOnAdd : true,
29135 * @event OverlayViewOnRemove
29136 * Fires when OverlayView Draw
29137 * @param {Roo.bootstrap.LocationPicker} this
29139 OverlayViewOnRemove : true,
29141 * @event OverlayViewShow
29142 * Fires when OverlayView Draw
29143 * @param {Roo.bootstrap.LocationPicker} this
29144 * @param {Pixel} cpx
29146 OverlayViewShow : true,
29148 * @event OverlayViewHide
29149 * Fires when OverlayView Draw
29150 * @param {Roo.bootstrap.LocationPicker} this
29152 OverlayViewHide : true,
29154 * @event loadexception
29155 * Fires when load google lib failed.
29156 * @param {Roo.bootstrap.LocationPicker} this
29158 loadexception : true
29163 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29165 gMapContext: false,
29171 mapTypeControl: false,
29172 disableDoubleClickZoom: false,
29174 streetViewControl: false,
29178 enableAutocomplete: false,
29179 enableReverseGeocode: true,
29182 getAutoCreate: function()
29187 cls: 'roo-location-picker'
29193 initEvents: function(ct, position)
29195 if(!this.el.getWidth() || this.isApplied()){
29199 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29204 initial: function()
29206 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29207 this.fireEvent('loadexception', this);
29211 if(!this.mapTypeId){
29212 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29215 this.gMapContext = this.GMapContext();
29217 this.initOverlayView();
29219 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29223 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29224 _this.setPosition(_this.gMapContext.marker.position);
29227 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29228 _this.fireEvent('mapClick', this, event);
29232 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29233 _this.fireEvent('mapRightClick', this, event);
29237 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29238 _this.fireEvent('markerClick', this, event);
29242 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29243 _this.fireEvent('markerRightClick', this, event);
29247 this.setPosition(this.gMapContext.location);
29249 this.fireEvent('initial', this, this.gMapContext.location);
29252 initOverlayView: function()
29256 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29260 _this.fireEvent('OverlayViewDraw', _this);
29265 _this.fireEvent('OverlayViewOnAdd', _this);
29268 onRemove: function()
29270 _this.fireEvent('OverlayViewOnRemove', _this);
29273 show: function(cpx)
29275 _this.fireEvent('OverlayViewShow', _this, cpx);
29280 _this.fireEvent('OverlayViewHide', _this);
29286 fromLatLngToContainerPixel: function(event)
29288 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29291 isApplied: function()
29293 return this.getGmapContext() == false ? false : true;
29296 getGmapContext: function()
29298 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29301 GMapContext: function()
29303 var position = new google.maps.LatLng(this.latitude, this.longitude);
29305 var _map = new google.maps.Map(this.el.dom, {
29308 mapTypeId: this.mapTypeId,
29309 mapTypeControl: this.mapTypeControl,
29310 disableDoubleClickZoom: this.disableDoubleClickZoom,
29311 scrollwheel: this.scrollwheel,
29312 streetViewControl: this.streetViewControl,
29313 locationName: this.locationName,
29314 draggable: this.draggable,
29315 enableAutocomplete: this.enableAutocomplete,
29316 enableReverseGeocode: this.enableReverseGeocode
29319 var _marker = new google.maps.Marker({
29320 position: position,
29322 title: this.markerTitle,
29323 draggable: this.draggable
29330 location: position,
29331 radius: this.radius,
29332 locationName: this.locationName,
29333 addressComponents: {
29334 formatted_address: null,
29335 addressLine1: null,
29336 addressLine2: null,
29338 streetNumber: null,
29342 stateOrProvince: null
29345 domContainer: this.el.dom,
29346 geodecoder: new google.maps.Geocoder()
29350 drawCircle: function(center, radius, options)
29352 if (this.gMapContext.circle != null) {
29353 this.gMapContext.circle.setMap(null);
29357 options = Roo.apply({}, options, {
29358 strokeColor: "#0000FF",
29359 strokeOpacity: .35,
29361 fillColor: "#0000FF",
29365 options.map = this.gMapContext.map;
29366 options.radius = radius;
29367 options.center = center;
29368 this.gMapContext.circle = new google.maps.Circle(options);
29369 return this.gMapContext.circle;
29375 setPosition: function(location)
29377 this.gMapContext.location = location;
29378 this.gMapContext.marker.setPosition(location);
29379 this.gMapContext.map.panTo(location);
29380 this.drawCircle(location, this.gMapContext.radius, {});
29384 if (this.gMapContext.settings.enableReverseGeocode) {
29385 this.gMapContext.geodecoder.geocode({
29386 latLng: this.gMapContext.location
29387 }, function(results, status) {
29389 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29390 _this.gMapContext.locationName = results[0].formatted_address;
29391 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29393 _this.fireEvent('positionchanged', this, location);
29400 this.fireEvent('positionchanged', this, location);
29405 google.maps.event.trigger(this.gMapContext.map, "resize");
29407 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29409 this.fireEvent('resize', this);
29412 setPositionByLatLng: function(latitude, longitude)
29414 this.setPosition(new google.maps.LatLng(latitude, longitude));
29417 getCurrentPosition: function()
29420 latitude: this.gMapContext.location.lat(),
29421 longitude: this.gMapContext.location.lng()
29425 getAddressName: function()
29427 return this.gMapContext.locationName;
29430 getAddressComponents: function()
29432 return this.gMapContext.addressComponents;
29435 address_component_from_google_geocode: function(address_components)
29439 for (var i = 0; i < address_components.length; i++) {
29440 var component = address_components[i];
29441 if (component.types.indexOf("postal_code") >= 0) {
29442 result.postalCode = component.short_name;
29443 } else if (component.types.indexOf("street_number") >= 0) {
29444 result.streetNumber = component.short_name;
29445 } else if (component.types.indexOf("route") >= 0) {
29446 result.streetName = component.short_name;
29447 } else if (component.types.indexOf("neighborhood") >= 0) {
29448 result.city = component.short_name;
29449 } else if (component.types.indexOf("locality") >= 0) {
29450 result.city = component.short_name;
29451 } else if (component.types.indexOf("sublocality") >= 0) {
29452 result.district = component.short_name;
29453 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29454 result.stateOrProvince = component.short_name;
29455 } else if (component.types.indexOf("country") >= 0) {
29456 result.country = component.short_name;
29460 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29461 result.addressLine2 = "";
29465 setZoomLevel: function(zoom)
29467 this.gMapContext.map.setZoom(zoom);
29480 this.fireEvent('show', this);
29491 this.fireEvent('hide', this);
29496 Roo.apply(Roo.bootstrap.LocationPicker, {
29498 OverlayView : function(map, options)
29500 options = options || {};
29507 * @class Roo.bootstrap.Alert
29508 * @extends Roo.bootstrap.Component
29509 * Bootstrap Alert class - shows an alert area box
29511 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29512 Enter a valid email address
29515 * @cfg {String} title The title of alert
29516 * @cfg {String} html The content of alert
29517 * @cfg {String} weight ( success | info | warning | danger )
29518 * @cfg {String} faicon font-awesomeicon
29521 * Create a new alert
29522 * @param {Object} config The config object
29526 Roo.bootstrap.Alert = function(config){
29527 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29531 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29538 getAutoCreate : function()
29547 cls : 'roo-alert-icon'
29552 cls : 'roo-alert-title',
29557 cls : 'roo-alert-text',
29564 cfg.cn[0].cls += ' fa ' + this.faicon;
29568 cfg.cls += ' alert-' + this.weight;
29574 initEvents: function()
29576 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29579 setTitle : function(str)
29581 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29584 setText : function(str)
29586 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29589 setWeight : function(weight)
29592 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29595 this.weight = weight;
29597 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29600 setIcon : function(icon)
29603 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29606 this.faicon = icon;
29608 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29629 * @class Roo.bootstrap.UploadCropbox
29630 * @extends Roo.bootstrap.Component
29631 * Bootstrap UploadCropbox class
29632 * @cfg {String} emptyText show when image has been loaded
29633 * @cfg {String} rotateNotify show when image too small to rotate
29634 * @cfg {Number} errorTimeout default 3000
29635 * @cfg {Number} minWidth default 300
29636 * @cfg {Number} minHeight default 300
29637 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29638 * @cfg {Boolean} isDocument (true|false) default false
29639 * @cfg {String} url action url
29640 * @cfg {String} paramName default 'imageUpload'
29641 * @cfg {String} method default POST
29642 * @cfg {Boolean} loadMask (true|false) default true
29643 * @cfg {Boolean} loadingText default 'Loading...'
29646 * Create a new UploadCropbox
29647 * @param {Object} config The config object
29650 Roo.bootstrap.UploadCropbox = function(config){
29651 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29655 * @event beforeselectfile
29656 * Fire before select file
29657 * @param {Roo.bootstrap.UploadCropbox} this
29659 "beforeselectfile" : true,
29662 * Fire after initEvent
29663 * @param {Roo.bootstrap.UploadCropbox} this
29668 * Fire after initEvent
29669 * @param {Roo.bootstrap.UploadCropbox} this
29670 * @param {String} data
29675 * Fire when preparing the file data
29676 * @param {Roo.bootstrap.UploadCropbox} this
29677 * @param {Object} file
29682 * Fire when get exception
29683 * @param {Roo.bootstrap.UploadCropbox} this
29684 * @param {XMLHttpRequest} xhr
29686 "exception" : true,
29688 * @event beforeloadcanvas
29689 * Fire before load the canvas
29690 * @param {Roo.bootstrap.UploadCropbox} this
29691 * @param {String} src
29693 "beforeloadcanvas" : true,
29696 * Fire when trash image
29697 * @param {Roo.bootstrap.UploadCropbox} this
29702 * Fire when download the image
29703 * @param {Roo.bootstrap.UploadCropbox} this
29707 * @event footerbuttonclick
29708 * Fire when footerbuttonclick
29709 * @param {Roo.bootstrap.UploadCropbox} this
29710 * @param {String} type
29712 "footerbuttonclick" : true,
29716 * @param {Roo.bootstrap.UploadCropbox} this
29721 * Fire when rotate the image
29722 * @param {Roo.bootstrap.UploadCropbox} this
29723 * @param {String} pos
29728 * Fire when inspect the file
29729 * @param {Roo.bootstrap.UploadCropbox} this
29730 * @param {Object} file
29735 * Fire when xhr upload the file
29736 * @param {Roo.bootstrap.UploadCropbox} this
29737 * @param {Object} data
29742 * Fire when arrange the file data
29743 * @param {Roo.bootstrap.UploadCropbox} this
29744 * @param {Object} formData
29749 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29752 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29754 emptyText : 'Click to upload image',
29755 rotateNotify : 'Image is too small to rotate',
29756 errorTimeout : 3000,
29770 cropType : 'image/jpeg',
29772 canvasLoaded : false,
29773 isDocument : false,
29775 paramName : 'imageUpload',
29777 loadingText : 'Loading...',
29780 getAutoCreate : function()
29784 cls : 'roo-upload-cropbox',
29788 cls : 'roo-upload-cropbox-selector',
29793 cls : 'roo-upload-cropbox-body',
29794 style : 'cursor:pointer',
29798 cls : 'roo-upload-cropbox-preview'
29802 cls : 'roo-upload-cropbox-thumb'
29806 cls : 'roo-upload-cropbox-empty-notify',
29807 html : this.emptyText
29811 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29812 html : this.rotateNotify
29818 cls : 'roo-upload-cropbox-footer',
29821 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29831 onRender : function(ct, position)
29833 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29835 if (this.buttons.length) {
29837 Roo.each(this.buttons, function(bb) {
29839 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29841 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29847 this.maskEl = this.el;
29851 initEvents : function()
29853 this.urlAPI = (window.createObjectURL && window) ||
29854 (window.URL && URL.revokeObjectURL && URL) ||
29855 (window.webkitURL && webkitURL);
29857 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29858 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29860 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29861 this.selectorEl.hide();
29863 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29864 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29866 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29867 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29868 this.thumbEl.hide();
29870 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29871 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29873 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29874 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29875 this.errorEl.hide();
29877 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29878 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29879 this.footerEl.hide();
29881 this.setThumbBoxSize();
29887 this.fireEvent('initial', this);
29894 window.addEventListener("resize", function() { _this.resize(); } );
29896 this.bodyEl.on('click', this.beforeSelectFile, this);
29899 this.bodyEl.on('touchstart', this.onTouchStart, this);
29900 this.bodyEl.on('touchmove', this.onTouchMove, this);
29901 this.bodyEl.on('touchend', this.onTouchEnd, this);
29905 this.bodyEl.on('mousedown', this.onMouseDown, this);
29906 this.bodyEl.on('mousemove', this.onMouseMove, this);
29907 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29908 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29909 Roo.get(document).on('mouseup', this.onMouseUp, this);
29912 this.selectorEl.on('change', this.onFileSelected, this);
29918 this.baseScale = 1;
29920 this.baseRotate = 1;
29921 this.dragable = false;
29922 this.pinching = false;
29925 this.cropData = false;
29926 this.notifyEl.dom.innerHTML = this.emptyText;
29928 this.selectorEl.dom.value = '';
29932 resize : function()
29934 if(this.fireEvent('resize', this) != false){
29935 this.setThumbBoxPosition();
29936 this.setCanvasPosition();
29940 onFooterButtonClick : function(e, el, o, type)
29943 case 'rotate-left' :
29944 this.onRotateLeft(e);
29946 case 'rotate-right' :
29947 this.onRotateRight(e);
29950 this.beforeSelectFile(e);
29965 this.fireEvent('footerbuttonclick', this, type);
29968 beforeSelectFile : function(e)
29970 e.preventDefault();
29972 if(this.fireEvent('beforeselectfile', this) != false){
29973 this.selectorEl.dom.click();
29977 onFileSelected : function(e)
29979 e.preventDefault();
29981 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29985 var file = this.selectorEl.dom.files[0];
29987 if(this.fireEvent('inspect', this, file) != false){
29988 this.prepare(file);
29993 trash : function(e)
29995 this.fireEvent('trash', this);
29998 download : function(e)
30000 this.fireEvent('download', this);
30003 loadCanvas : function(src)
30005 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30009 this.imageEl = document.createElement('img');
30013 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30015 this.imageEl.src = src;
30019 onLoadCanvas : function()
30021 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30022 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30024 this.bodyEl.un('click', this.beforeSelectFile, this);
30026 this.notifyEl.hide();
30027 this.thumbEl.show();
30028 this.footerEl.show();
30030 this.baseRotateLevel();
30032 if(this.isDocument){
30033 this.setThumbBoxSize();
30036 this.setThumbBoxPosition();
30038 this.baseScaleLevel();
30044 this.canvasLoaded = true;
30047 this.maskEl.unmask();
30052 setCanvasPosition : function()
30054 if(!this.canvasEl){
30058 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30059 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30061 this.previewEl.setLeft(pw);
30062 this.previewEl.setTop(ph);
30066 onMouseDown : function(e)
30070 this.dragable = true;
30071 this.pinching = false;
30073 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30074 this.dragable = false;
30078 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30079 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30083 onMouseMove : function(e)
30087 if(!this.canvasLoaded){
30091 if (!this.dragable){
30095 var minX = Math.ceil(this.thumbEl.getLeft(true));
30096 var minY = Math.ceil(this.thumbEl.getTop(true));
30098 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30099 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30101 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30102 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30104 x = x - this.mouseX;
30105 y = y - this.mouseY;
30107 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30108 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30110 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30111 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30113 this.previewEl.setLeft(bgX);
30114 this.previewEl.setTop(bgY);
30116 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30117 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30120 onMouseUp : function(e)
30124 this.dragable = false;
30127 onMouseWheel : function(e)
30131 this.startScale = this.scale;
30133 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30135 if(!this.zoomable()){
30136 this.scale = this.startScale;
30145 zoomable : function()
30147 var minScale = this.thumbEl.getWidth() / this.minWidth;
30149 if(this.minWidth < this.minHeight){
30150 minScale = this.thumbEl.getHeight() / this.minHeight;
30153 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30154 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30158 (this.rotate == 0 || this.rotate == 180) &&
30160 width > this.imageEl.OriginWidth ||
30161 height > this.imageEl.OriginHeight ||
30162 (width < this.minWidth && height < this.minHeight)
30170 (this.rotate == 90 || this.rotate == 270) &&
30172 width > this.imageEl.OriginWidth ||
30173 height > this.imageEl.OriginHeight ||
30174 (width < this.minHeight && height < this.minWidth)
30181 !this.isDocument &&
30182 (this.rotate == 0 || this.rotate == 180) &&
30184 width < this.minWidth ||
30185 width > this.imageEl.OriginWidth ||
30186 height < this.minHeight ||
30187 height > this.imageEl.OriginHeight
30194 !this.isDocument &&
30195 (this.rotate == 90 || this.rotate == 270) &&
30197 width < this.minHeight ||
30198 width > this.imageEl.OriginWidth ||
30199 height < this.minWidth ||
30200 height > this.imageEl.OriginHeight
30210 onRotateLeft : function(e)
30212 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30214 var minScale = this.thumbEl.getWidth() / this.minWidth;
30216 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30217 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30219 this.startScale = this.scale;
30221 while (this.getScaleLevel() < minScale){
30223 this.scale = this.scale + 1;
30225 if(!this.zoomable()){
30230 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30231 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30236 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30243 this.scale = this.startScale;
30245 this.onRotateFail();
30250 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30252 if(this.isDocument){
30253 this.setThumbBoxSize();
30254 this.setThumbBoxPosition();
30255 this.setCanvasPosition();
30260 this.fireEvent('rotate', this, 'left');
30264 onRotateRight : function(e)
30266 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30268 var minScale = this.thumbEl.getWidth() / this.minWidth;
30270 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30271 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30273 this.startScale = this.scale;
30275 while (this.getScaleLevel() < minScale){
30277 this.scale = this.scale + 1;
30279 if(!this.zoomable()){
30284 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30285 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30290 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30297 this.scale = this.startScale;
30299 this.onRotateFail();
30304 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30306 if(this.isDocument){
30307 this.setThumbBoxSize();
30308 this.setThumbBoxPosition();
30309 this.setCanvasPosition();
30314 this.fireEvent('rotate', this, 'right');
30317 onRotateFail : function()
30319 this.errorEl.show(true);
30323 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30328 this.previewEl.dom.innerHTML = '';
30330 var canvasEl = document.createElement("canvas");
30332 var contextEl = canvasEl.getContext("2d");
30334 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30335 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30336 var center = this.imageEl.OriginWidth / 2;
30338 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30339 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30340 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30341 center = this.imageEl.OriginHeight / 2;
30344 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30346 contextEl.translate(center, center);
30347 contextEl.rotate(this.rotate * Math.PI / 180);
30349 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30351 this.canvasEl = document.createElement("canvas");
30353 this.contextEl = this.canvasEl.getContext("2d");
30355 switch (this.rotate) {
30358 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30359 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30361 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30366 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30367 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30369 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30370 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);
30374 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30379 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30380 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30382 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30383 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);
30387 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);
30392 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30393 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30395 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30396 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30400 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);
30407 this.previewEl.appendChild(this.canvasEl);
30409 this.setCanvasPosition();
30414 if(!this.canvasLoaded){
30418 var imageCanvas = document.createElement("canvas");
30420 var imageContext = imageCanvas.getContext("2d");
30422 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30423 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30425 var center = imageCanvas.width / 2;
30427 imageContext.translate(center, center);
30429 imageContext.rotate(this.rotate * Math.PI / 180);
30431 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30433 var canvas = document.createElement("canvas");
30435 var context = canvas.getContext("2d");
30437 canvas.width = this.minWidth;
30438 canvas.height = this.minHeight;
30440 switch (this.rotate) {
30443 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30444 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30446 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30447 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30449 var targetWidth = this.minWidth - 2 * x;
30450 var targetHeight = this.minHeight - 2 * y;
30454 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30455 scale = targetWidth / width;
30458 if(x > 0 && y == 0){
30459 scale = targetHeight / height;
30462 if(x > 0 && y > 0){
30463 scale = targetWidth / width;
30465 if(width < height){
30466 scale = targetHeight / height;
30470 context.scale(scale, scale);
30472 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30473 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30475 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30476 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30478 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30483 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30484 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30486 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30487 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30489 var targetWidth = this.minWidth - 2 * x;
30490 var targetHeight = this.minHeight - 2 * y;
30494 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30495 scale = targetWidth / width;
30498 if(x > 0 && y == 0){
30499 scale = targetHeight / height;
30502 if(x > 0 && y > 0){
30503 scale = targetWidth / width;
30505 if(width < height){
30506 scale = targetHeight / height;
30510 context.scale(scale, scale);
30512 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30513 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30515 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30516 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30518 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30520 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30525 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30526 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30528 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30529 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30531 var targetWidth = this.minWidth - 2 * x;
30532 var targetHeight = this.minHeight - 2 * y;
30536 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30537 scale = targetWidth / width;
30540 if(x > 0 && y == 0){
30541 scale = targetHeight / height;
30544 if(x > 0 && y > 0){
30545 scale = targetWidth / width;
30547 if(width < height){
30548 scale = targetHeight / height;
30552 context.scale(scale, scale);
30554 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30555 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30557 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30558 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30560 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30561 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30563 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30568 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30569 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30571 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30572 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30574 var targetWidth = this.minWidth - 2 * x;
30575 var targetHeight = this.minHeight - 2 * y;
30579 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30580 scale = targetWidth / width;
30583 if(x > 0 && y == 0){
30584 scale = targetHeight / height;
30587 if(x > 0 && y > 0){
30588 scale = targetWidth / width;
30590 if(width < height){
30591 scale = targetHeight / height;
30595 context.scale(scale, scale);
30597 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30598 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30600 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30601 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30603 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30605 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30612 this.cropData = canvas.toDataURL(this.cropType);
30614 if(this.fireEvent('crop', this, this.cropData) !== false){
30615 this.process(this.file, this.cropData);
30622 setThumbBoxSize : function()
30626 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30627 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30628 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30630 this.minWidth = width;
30631 this.minHeight = height;
30633 if(this.rotate == 90 || this.rotate == 270){
30634 this.minWidth = height;
30635 this.minHeight = width;
30640 width = Math.ceil(this.minWidth * height / this.minHeight);
30642 if(this.minWidth > this.minHeight){
30644 height = Math.ceil(this.minHeight * width / this.minWidth);
30647 this.thumbEl.setStyle({
30648 width : width + 'px',
30649 height : height + 'px'
30656 setThumbBoxPosition : function()
30658 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30659 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30661 this.thumbEl.setLeft(x);
30662 this.thumbEl.setTop(y);
30666 baseRotateLevel : function()
30668 this.baseRotate = 1;
30671 typeof(this.exif) != 'undefined' &&
30672 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30673 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30675 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30678 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30682 baseScaleLevel : function()
30686 if(this.isDocument){
30688 if(this.baseRotate == 6 || this.baseRotate == 8){
30690 height = this.thumbEl.getHeight();
30691 this.baseScale = height / this.imageEl.OriginWidth;
30693 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30694 width = this.thumbEl.getWidth();
30695 this.baseScale = width / this.imageEl.OriginHeight;
30701 height = this.thumbEl.getHeight();
30702 this.baseScale = height / this.imageEl.OriginHeight;
30704 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30705 width = this.thumbEl.getWidth();
30706 this.baseScale = width / this.imageEl.OriginWidth;
30712 if(this.baseRotate == 6 || this.baseRotate == 8){
30714 width = this.thumbEl.getHeight();
30715 this.baseScale = width / this.imageEl.OriginHeight;
30717 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30718 height = this.thumbEl.getWidth();
30719 this.baseScale = height / this.imageEl.OriginHeight;
30722 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30723 height = this.thumbEl.getWidth();
30724 this.baseScale = height / this.imageEl.OriginHeight;
30726 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30727 width = this.thumbEl.getHeight();
30728 this.baseScale = width / this.imageEl.OriginWidth;
30735 width = this.thumbEl.getWidth();
30736 this.baseScale = width / this.imageEl.OriginWidth;
30738 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30739 height = this.thumbEl.getHeight();
30740 this.baseScale = height / this.imageEl.OriginHeight;
30743 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30745 height = this.thumbEl.getHeight();
30746 this.baseScale = height / this.imageEl.OriginHeight;
30748 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30749 width = this.thumbEl.getWidth();
30750 this.baseScale = width / this.imageEl.OriginWidth;
30758 getScaleLevel : function()
30760 return this.baseScale * Math.pow(1.1, this.scale);
30763 onTouchStart : function(e)
30765 if(!this.canvasLoaded){
30766 this.beforeSelectFile(e);
30770 var touches = e.browserEvent.touches;
30776 if(touches.length == 1){
30777 this.onMouseDown(e);
30781 if(touches.length != 2){
30787 for(var i = 0, finger; finger = touches[i]; i++){
30788 coords.push(finger.pageX, finger.pageY);
30791 var x = Math.pow(coords[0] - coords[2], 2);
30792 var y = Math.pow(coords[1] - coords[3], 2);
30794 this.startDistance = Math.sqrt(x + y);
30796 this.startScale = this.scale;
30798 this.pinching = true;
30799 this.dragable = false;
30803 onTouchMove : function(e)
30805 if(!this.pinching && !this.dragable){
30809 var touches = e.browserEvent.touches;
30816 this.onMouseMove(e);
30822 for(var i = 0, finger; finger = touches[i]; i++){
30823 coords.push(finger.pageX, finger.pageY);
30826 var x = Math.pow(coords[0] - coords[2], 2);
30827 var y = Math.pow(coords[1] - coords[3], 2);
30829 this.endDistance = Math.sqrt(x + y);
30831 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30833 if(!this.zoomable()){
30834 this.scale = this.startScale;
30842 onTouchEnd : function(e)
30844 this.pinching = false;
30845 this.dragable = false;
30849 process : function(file, crop)
30852 this.maskEl.mask(this.loadingText);
30855 this.xhr = new XMLHttpRequest();
30857 file.xhr = this.xhr;
30859 this.xhr.open(this.method, this.url, true);
30862 "Accept": "application/json",
30863 "Cache-Control": "no-cache",
30864 "X-Requested-With": "XMLHttpRequest"
30867 for (var headerName in headers) {
30868 var headerValue = headers[headerName];
30870 this.xhr.setRequestHeader(headerName, headerValue);
30876 this.xhr.onload = function()
30878 _this.xhrOnLoad(_this.xhr);
30881 this.xhr.onerror = function()
30883 _this.xhrOnError(_this.xhr);
30886 var formData = new FormData();
30888 formData.append('returnHTML', 'NO');
30891 formData.append('crop', crop);
30894 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30895 formData.append(this.paramName, file, file.name);
30898 if(typeof(file.filename) != 'undefined'){
30899 formData.append('filename', file.filename);
30902 if(typeof(file.mimetype) != 'undefined'){
30903 formData.append('mimetype', file.mimetype);
30906 if(this.fireEvent('arrange', this, formData) != false){
30907 this.xhr.send(formData);
30911 xhrOnLoad : function(xhr)
30914 this.maskEl.unmask();
30917 if (xhr.readyState !== 4) {
30918 this.fireEvent('exception', this, xhr);
30922 var response = Roo.decode(xhr.responseText);
30924 if(!response.success){
30925 this.fireEvent('exception', this, xhr);
30929 var response = Roo.decode(xhr.responseText);
30931 this.fireEvent('upload', this, response);
30935 xhrOnError : function()
30938 this.maskEl.unmask();
30941 Roo.log('xhr on error');
30943 var response = Roo.decode(xhr.responseText);
30949 prepare : function(file)
30952 this.maskEl.mask(this.loadingText);
30958 if(typeof(file) === 'string'){
30959 this.loadCanvas(file);
30963 if(!file || !this.urlAPI){
30968 this.cropType = file.type;
30972 if(this.fireEvent('prepare', this, this.file) != false){
30974 var reader = new FileReader();
30976 reader.onload = function (e) {
30977 if (e.target.error) {
30978 Roo.log(e.target.error);
30982 var buffer = e.target.result,
30983 dataView = new DataView(buffer),
30985 maxOffset = dataView.byteLength - 4,
30989 if (dataView.getUint16(0) === 0xffd8) {
30990 while (offset < maxOffset) {
30991 markerBytes = dataView.getUint16(offset);
30993 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30994 markerLength = dataView.getUint16(offset + 2) + 2;
30995 if (offset + markerLength > dataView.byteLength) {
30996 Roo.log('Invalid meta data: Invalid segment size.');
31000 if(markerBytes == 0xffe1){
31001 _this.parseExifData(
31008 offset += markerLength;
31018 var url = _this.urlAPI.createObjectURL(_this.file);
31020 _this.loadCanvas(url);
31025 reader.readAsArrayBuffer(this.file);
31031 parseExifData : function(dataView, offset, length)
31033 var tiffOffset = offset + 10,
31037 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31038 // No Exif data, might be XMP data instead
31042 // Check for the ASCII code for "Exif" (0x45786966):
31043 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31044 // No Exif data, might be XMP data instead
31047 if (tiffOffset + 8 > dataView.byteLength) {
31048 Roo.log('Invalid Exif data: Invalid segment size.');
31051 // Check for the two null bytes:
31052 if (dataView.getUint16(offset + 8) !== 0x0000) {
31053 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31056 // Check the byte alignment:
31057 switch (dataView.getUint16(tiffOffset)) {
31059 littleEndian = true;
31062 littleEndian = false;
31065 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31068 // Check for the TIFF tag marker (0x002A):
31069 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31070 Roo.log('Invalid Exif data: Missing TIFF marker.');
31073 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31074 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31076 this.parseExifTags(
31079 tiffOffset + dirOffset,
31084 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31089 if (dirOffset + 6 > dataView.byteLength) {
31090 Roo.log('Invalid Exif data: Invalid directory offset.');
31093 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31094 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31095 if (dirEndOffset + 4 > dataView.byteLength) {
31096 Roo.log('Invalid Exif data: Invalid directory size.');
31099 for (i = 0; i < tagsNumber; i += 1) {
31103 dirOffset + 2 + 12 * i, // tag offset
31107 // Return the offset to the next directory:
31108 return dataView.getUint32(dirEndOffset, littleEndian);
31111 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31113 var tag = dataView.getUint16(offset, littleEndian);
31115 this.exif[tag] = this.getExifValue(
31119 dataView.getUint16(offset + 2, littleEndian), // tag type
31120 dataView.getUint32(offset + 4, littleEndian), // tag length
31125 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31127 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31136 Roo.log('Invalid Exif data: Invalid tag type.');
31140 tagSize = tagType.size * length;
31141 // Determine if the value is contained in the dataOffset bytes,
31142 // or if the value at the dataOffset is a pointer to the actual data:
31143 dataOffset = tagSize > 4 ?
31144 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31145 if (dataOffset + tagSize > dataView.byteLength) {
31146 Roo.log('Invalid Exif data: Invalid data offset.');
31149 if (length === 1) {
31150 return tagType.getValue(dataView, dataOffset, littleEndian);
31153 for (i = 0; i < length; i += 1) {
31154 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31157 if (tagType.ascii) {
31159 // Concatenate the chars:
31160 for (i = 0; i < values.length; i += 1) {
31162 // Ignore the terminating NULL byte(s):
31163 if (c === '\u0000') {
31175 Roo.apply(Roo.bootstrap.UploadCropbox, {
31177 'Orientation': 0x0112
31181 1: 0, //'top-left',
31183 3: 180, //'bottom-right',
31184 // 4: 'bottom-left',
31186 6: 90, //'right-top',
31187 // 7: 'right-bottom',
31188 8: 270 //'left-bottom'
31192 // byte, 8-bit unsigned int:
31194 getValue: function (dataView, dataOffset) {
31195 return dataView.getUint8(dataOffset);
31199 // ascii, 8-bit byte:
31201 getValue: function (dataView, dataOffset) {
31202 return String.fromCharCode(dataView.getUint8(dataOffset));
31207 // short, 16 bit int:
31209 getValue: function (dataView, dataOffset, littleEndian) {
31210 return dataView.getUint16(dataOffset, littleEndian);
31214 // long, 32 bit int:
31216 getValue: function (dataView, dataOffset, littleEndian) {
31217 return dataView.getUint32(dataOffset, littleEndian);
31221 // rational = two long values, first is numerator, second is denominator:
31223 getValue: function (dataView, dataOffset, littleEndian) {
31224 return dataView.getUint32(dataOffset, littleEndian) /
31225 dataView.getUint32(dataOffset + 4, littleEndian);
31229 // slong, 32 bit signed int:
31231 getValue: function (dataView, dataOffset, littleEndian) {
31232 return dataView.getInt32(dataOffset, littleEndian);
31236 // srational, two slongs, first is numerator, second is denominator:
31238 getValue: function (dataView, dataOffset, littleEndian) {
31239 return dataView.getInt32(dataOffset, littleEndian) /
31240 dataView.getInt32(dataOffset + 4, littleEndian);
31250 cls : 'btn-group roo-upload-cropbox-rotate-left',
31251 action : 'rotate-left',
31255 cls : 'btn btn-default',
31256 html : '<i class="fa fa-undo"></i>'
31262 cls : 'btn-group roo-upload-cropbox-picture',
31263 action : 'picture',
31267 cls : 'btn btn-default',
31268 html : '<i class="fa fa-picture-o"></i>'
31274 cls : 'btn-group roo-upload-cropbox-rotate-right',
31275 action : 'rotate-right',
31279 cls : 'btn btn-default',
31280 html : '<i class="fa fa-repeat"></i>'
31288 cls : 'btn-group roo-upload-cropbox-rotate-left',
31289 action : 'rotate-left',
31293 cls : 'btn btn-default',
31294 html : '<i class="fa fa-undo"></i>'
31300 cls : 'btn-group roo-upload-cropbox-download',
31301 action : 'download',
31305 cls : 'btn btn-default',
31306 html : '<i class="fa fa-download"></i>'
31312 cls : 'btn-group roo-upload-cropbox-crop',
31317 cls : 'btn btn-default',
31318 html : '<i class="fa fa-crop"></i>'
31324 cls : 'btn-group roo-upload-cropbox-trash',
31329 cls : 'btn btn-default',
31330 html : '<i class="fa fa-trash"></i>'
31336 cls : 'btn-group roo-upload-cropbox-rotate-right',
31337 action : 'rotate-right',
31341 cls : 'btn btn-default',
31342 html : '<i class="fa fa-repeat"></i>'
31350 cls : 'btn-group roo-upload-cropbox-rotate-left',
31351 action : 'rotate-left',
31355 cls : 'btn btn-default',
31356 html : '<i class="fa fa-undo"></i>'
31362 cls : 'btn-group roo-upload-cropbox-rotate-right',
31363 action : 'rotate-right',
31367 cls : 'btn btn-default',
31368 html : '<i class="fa fa-repeat"></i>'
31381 * @class Roo.bootstrap.DocumentManager
31382 * @extends Roo.bootstrap.Component
31383 * Bootstrap DocumentManager class
31384 * @cfg {String} paramName default 'imageUpload'
31385 * @cfg {String} toolTipName default 'filename'
31386 * @cfg {String} method default POST
31387 * @cfg {String} url action url
31388 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31389 * @cfg {Boolean} multiple multiple upload default true
31390 * @cfg {Number} thumbSize default 300
31391 * @cfg {String} fieldLabel
31392 * @cfg {Number} labelWidth default 4
31393 * @cfg {String} labelAlign (left|top) default left
31394 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31395 * @cfg {Number} labellg set the width of label (1-12)
31396 * @cfg {Number} labelmd set the width of label (1-12)
31397 * @cfg {Number} labelsm set the width of label (1-12)
31398 * @cfg {Number} labelxs set the width of label (1-12)
31401 * Create a new DocumentManager
31402 * @param {Object} config The config object
31405 Roo.bootstrap.DocumentManager = function(config){
31406 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31409 this.delegates = [];
31414 * Fire when initial the DocumentManager
31415 * @param {Roo.bootstrap.DocumentManager} this
31420 * inspect selected file
31421 * @param {Roo.bootstrap.DocumentManager} this
31422 * @param {File} file
31427 * Fire when xhr load exception
31428 * @param {Roo.bootstrap.DocumentManager} this
31429 * @param {XMLHttpRequest} xhr
31431 "exception" : true,
31433 * @event afterupload
31434 * Fire when xhr load exception
31435 * @param {Roo.bootstrap.DocumentManager} this
31436 * @param {XMLHttpRequest} xhr
31438 "afterupload" : true,
31441 * prepare the form data
31442 * @param {Roo.bootstrap.DocumentManager} this
31443 * @param {Object} formData
31448 * Fire when remove the file
31449 * @param {Roo.bootstrap.DocumentManager} this
31450 * @param {Object} file
31455 * Fire after refresh the file
31456 * @param {Roo.bootstrap.DocumentManager} this
31461 * Fire after click the image
31462 * @param {Roo.bootstrap.DocumentManager} this
31463 * @param {Object} file
31468 * Fire when upload a image and editable set to true
31469 * @param {Roo.bootstrap.DocumentManager} this
31470 * @param {Object} file
31474 * @event beforeselectfile
31475 * Fire before select file
31476 * @param {Roo.bootstrap.DocumentManager} this
31478 "beforeselectfile" : true,
31481 * Fire before process file
31482 * @param {Roo.bootstrap.DocumentManager} this
31483 * @param {Object} file
31487 * @event previewrendered
31488 * Fire when preview rendered
31489 * @param {Roo.bootstrap.DocumentManager} this
31490 * @param {Object} file
31492 "previewrendered" : true,
31495 "previewResize" : true
31500 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31509 paramName : 'imageUpload',
31510 toolTipName : 'filename',
31513 labelAlign : 'left',
31523 getAutoCreate : function()
31525 var managerWidget = {
31527 cls : 'roo-document-manager',
31531 cls : 'roo-document-manager-selector',
31536 cls : 'roo-document-manager-uploader',
31540 cls : 'roo-document-manager-upload-btn',
31541 html : '<i class="fa fa-plus"></i>'
31552 cls : 'column col-md-12',
31557 if(this.fieldLabel.length){
31562 cls : 'column col-md-12',
31563 html : this.fieldLabel
31567 cls : 'column col-md-12',
31572 if(this.labelAlign == 'left'){
31577 html : this.fieldLabel
31586 if(this.labelWidth > 12){
31587 content[0].style = "width: " + this.labelWidth + 'px';
31590 if(this.labelWidth < 13 && this.labelmd == 0){
31591 this.labelmd = this.labelWidth;
31594 if(this.labellg > 0){
31595 content[0].cls += ' col-lg-' + this.labellg;
31596 content[1].cls += ' col-lg-' + (12 - this.labellg);
31599 if(this.labelmd > 0){
31600 content[0].cls += ' col-md-' + this.labelmd;
31601 content[1].cls += ' col-md-' + (12 - this.labelmd);
31604 if(this.labelsm > 0){
31605 content[0].cls += ' col-sm-' + this.labelsm;
31606 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31609 if(this.labelxs > 0){
31610 content[0].cls += ' col-xs-' + this.labelxs;
31611 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31619 cls : 'row clearfix',
31627 initEvents : function()
31629 this.managerEl = this.el.select('.roo-document-manager', true).first();
31630 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31632 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31633 this.selectorEl.hide();
31636 this.selectorEl.attr('multiple', 'multiple');
31639 this.selectorEl.on('change', this.onFileSelected, this);
31641 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31642 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31644 this.uploader.on('click', this.onUploaderClick, this);
31646 this.renderProgressDialog();
31650 window.addEventListener("resize", function() { _this.refresh(); } );
31652 this.fireEvent('initial', this);
31655 renderProgressDialog : function()
31659 this.progressDialog = new Roo.bootstrap.Modal({
31660 cls : 'roo-document-manager-progress-dialog',
31661 allow_close : false,
31672 btnclick : function() {
31673 _this.uploadCancel();
31679 this.progressDialog.render(Roo.get(document.body));
31681 this.progress = new Roo.bootstrap.Progress({
31682 cls : 'roo-document-manager-progress',
31687 this.progress.render(this.progressDialog.getChildContainer());
31689 this.progressBar = new Roo.bootstrap.ProgressBar({
31690 cls : 'roo-document-manager-progress-bar',
31693 aria_valuemax : 12,
31697 this.progressBar.render(this.progress.getChildContainer());
31700 onUploaderClick : function(e)
31702 e.preventDefault();
31704 if(this.fireEvent('beforeselectfile', this) != false){
31705 this.selectorEl.dom.click();
31710 onFileSelected : function(e)
31712 e.preventDefault();
31714 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31718 Roo.each(this.selectorEl.dom.files, function(file){
31719 if(this.fireEvent('inspect', this, file) != false){
31720 this.files.push(file);
31730 this.selectorEl.dom.value = '';
31732 if(!this.files || !this.files.length){
31736 if(this.boxes > 0 && this.files.length > this.boxes){
31737 this.files = this.files.slice(0, this.boxes);
31740 this.uploader.show();
31742 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31743 this.uploader.hide();
31752 Roo.each(this.files, function(file){
31754 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31755 var f = this.renderPreview(file);
31760 if(file.type.indexOf('image') != -1){
31761 this.delegates.push(
31763 _this.process(file);
31764 }).createDelegate(this)
31772 _this.process(file);
31773 }).createDelegate(this)
31778 this.files = files;
31780 this.delegates = this.delegates.concat(docs);
31782 if(!this.delegates.length){
31787 this.progressBar.aria_valuemax = this.delegates.length;
31794 arrange : function()
31796 if(!this.delegates.length){
31797 this.progressDialog.hide();
31802 var delegate = this.delegates.shift();
31804 this.progressDialog.show();
31806 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31808 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31813 refresh : function()
31815 this.uploader.show();
31817 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31818 this.uploader.hide();
31821 Roo.isTouch ? this.closable(false) : this.closable(true);
31823 this.fireEvent('refresh', this);
31826 onRemove : function(e, el, o)
31828 e.preventDefault();
31830 this.fireEvent('remove', this, o);
31834 remove : function(o)
31838 Roo.each(this.files, function(file){
31839 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31848 this.files = files;
31855 Roo.each(this.files, function(file){
31860 file.target.remove();
31869 onClick : function(e, el, o)
31871 e.preventDefault();
31873 this.fireEvent('click', this, o);
31877 closable : function(closable)
31879 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31881 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31893 xhrOnLoad : function(xhr)
31895 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31899 if (xhr.readyState !== 4) {
31901 this.fireEvent('exception', this, xhr);
31905 var response = Roo.decode(xhr.responseText);
31907 if(!response.success){
31909 this.fireEvent('exception', this, xhr);
31913 var file = this.renderPreview(response.data);
31915 this.files.push(file);
31919 this.fireEvent('afterupload', this, xhr);
31923 xhrOnError : function(xhr)
31925 Roo.log('xhr on error');
31927 var response = Roo.decode(xhr.responseText);
31934 process : function(file)
31936 if(this.fireEvent('process', this, file) !== false){
31937 if(this.editable && file.type.indexOf('image') != -1){
31938 this.fireEvent('edit', this, file);
31942 this.uploadStart(file, false);
31949 uploadStart : function(file, crop)
31951 this.xhr = new XMLHttpRequest();
31953 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31958 file.xhr = this.xhr;
31960 this.managerEl.createChild({
31962 cls : 'roo-document-manager-loading',
31966 tooltip : file.name,
31967 cls : 'roo-document-manager-thumb',
31968 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31974 this.xhr.open(this.method, this.url, true);
31977 "Accept": "application/json",
31978 "Cache-Control": "no-cache",
31979 "X-Requested-With": "XMLHttpRequest"
31982 for (var headerName in headers) {
31983 var headerValue = headers[headerName];
31985 this.xhr.setRequestHeader(headerName, headerValue);
31991 this.xhr.onload = function()
31993 _this.xhrOnLoad(_this.xhr);
31996 this.xhr.onerror = function()
31998 _this.xhrOnError(_this.xhr);
32001 var formData = new FormData();
32003 formData.append('returnHTML', 'NO');
32006 formData.append('crop', crop);
32009 formData.append(this.paramName, file, file.name);
32016 if(this.fireEvent('prepare', this, formData, options) != false){
32018 if(options.manually){
32022 this.xhr.send(formData);
32026 this.uploadCancel();
32029 uploadCancel : function()
32035 this.delegates = [];
32037 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32044 renderPreview : function(file)
32046 if(typeof(file.target) != 'undefined' && file.target){
32050 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32052 var previewEl = this.managerEl.createChild({
32054 cls : 'roo-document-manager-preview',
32058 tooltip : file[this.toolTipName],
32059 cls : 'roo-document-manager-thumb',
32060 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32065 html : '<i class="fa fa-times-circle"></i>'
32070 var close = previewEl.select('button.close', true).first();
32072 close.on('click', this.onRemove, this, file);
32074 file.target = previewEl;
32076 var image = previewEl.select('img', true).first();
32080 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32082 image.on('click', this.onClick, this, file);
32084 this.fireEvent('previewrendered', this, file);
32090 onPreviewLoad : function(file, image)
32092 if(typeof(file.target) == 'undefined' || !file.target){
32096 var width = image.dom.naturalWidth || image.dom.width;
32097 var height = image.dom.naturalHeight || image.dom.height;
32099 if(!this.previewResize) {
32103 if(width > height){
32104 file.target.addClass('wide');
32108 file.target.addClass('tall');
32113 uploadFromSource : function(file, crop)
32115 this.xhr = new XMLHttpRequest();
32117 this.managerEl.createChild({
32119 cls : 'roo-document-manager-loading',
32123 tooltip : file.name,
32124 cls : 'roo-document-manager-thumb',
32125 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32131 this.xhr.open(this.method, this.url, true);
32134 "Accept": "application/json",
32135 "Cache-Control": "no-cache",
32136 "X-Requested-With": "XMLHttpRequest"
32139 for (var headerName in headers) {
32140 var headerValue = headers[headerName];
32142 this.xhr.setRequestHeader(headerName, headerValue);
32148 this.xhr.onload = function()
32150 _this.xhrOnLoad(_this.xhr);
32153 this.xhr.onerror = function()
32155 _this.xhrOnError(_this.xhr);
32158 var formData = new FormData();
32160 formData.append('returnHTML', 'NO');
32162 formData.append('crop', crop);
32164 if(typeof(file.filename) != 'undefined'){
32165 formData.append('filename', file.filename);
32168 if(typeof(file.mimetype) != 'undefined'){
32169 formData.append('mimetype', file.mimetype);
32174 if(this.fireEvent('prepare', this, formData) != false){
32175 this.xhr.send(formData);
32185 * @class Roo.bootstrap.DocumentViewer
32186 * @extends Roo.bootstrap.Component
32187 * Bootstrap DocumentViewer class
32188 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32189 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32192 * Create a new DocumentViewer
32193 * @param {Object} config The config object
32196 Roo.bootstrap.DocumentViewer = function(config){
32197 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32202 * Fire after initEvent
32203 * @param {Roo.bootstrap.DocumentViewer} this
32209 * @param {Roo.bootstrap.DocumentViewer} this
32214 * Fire after download button
32215 * @param {Roo.bootstrap.DocumentViewer} this
32220 * Fire after trash button
32221 * @param {Roo.bootstrap.DocumentViewer} this
32228 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32230 showDownload : true,
32234 getAutoCreate : function()
32238 cls : 'roo-document-viewer',
32242 cls : 'roo-document-viewer-body',
32246 cls : 'roo-document-viewer-thumb',
32250 cls : 'roo-document-viewer-image'
32258 cls : 'roo-document-viewer-footer',
32261 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32265 cls : 'btn-group roo-document-viewer-download',
32269 cls : 'btn btn-default',
32270 html : '<i class="fa fa-download"></i>'
32276 cls : 'btn-group roo-document-viewer-trash',
32280 cls : 'btn btn-default',
32281 html : '<i class="fa fa-trash"></i>'
32294 initEvents : function()
32296 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32297 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32299 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32300 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32302 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32303 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32305 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32306 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32308 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32309 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32311 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32312 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32314 this.bodyEl.on('click', this.onClick, this);
32315 this.downloadBtn.on('click', this.onDownload, this);
32316 this.trashBtn.on('click', this.onTrash, this);
32318 this.downloadBtn.hide();
32319 this.trashBtn.hide();
32321 if(this.showDownload){
32322 this.downloadBtn.show();
32325 if(this.showTrash){
32326 this.trashBtn.show();
32329 if(!this.showDownload && !this.showTrash) {
32330 this.footerEl.hide();
32335 initial : function()
32337 this.fireEvent('initial', this);
32341 onClick : function(e)
32343 e.preventDefault();
32345 this.fireEvent('click', this);
32348 onDownload : function(e)
32350 e.preventDefault();
32352 this.fireEvent('download', this);
32355 onTrash : function(e)
32357 e.preventDefault();
32359 this.fireEvent('trash', this);
32371 * @class Roo.bootstrap.NavProgressBar
32372 * @extends Roo.bootstrap.Component
32373 * Bootstrap NavProgressBar class
32376 * Create a new nav progress bar
32377 * @param {Object} config The config object
32380 Roo.bootstrap.NavProgressBar = function(config){
32381 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32383 this.bullets = this.bullets || [];
32385 // Roo.bootstrap.NavProgressBar.register(this);
32389 * Fires when the active item changes
32390 * @param {Roo.bootstrap.NavProgressBar} this
32391 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32392 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32399 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32404 getAutoCreate : function()
32406 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32410 cls : 'roo-navigation-bar-group',
32414 cls : 'roo-navigation-top-bar'
32418 cls : 'roo-navigation-bullets-bar',
32422 cls : 'roo-navigation-bar'
32429 cls : 'roo-navigation-bottom-bar'
32439 initEvents: function()
32444 onRender : function(ct, position)
32446 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32448 if(this.bullets.length){
32449 Roo.each(this.bullets, function(b){
32458 addItem : function(cfg)
32460 var item = new Roo.bootstrap.NavProgressItem(cfg);
32462 item.parentId = this.id;
32463 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32466 var top = new Roo.bootstrap.Element({
32468 cls : 'roo-navigation-bar-text'
32471 var bottom = new Roo.bootstrap.Element({
32473 cls : 'roo-navigation-bar-text'
32476 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32477 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32479 var topText = new Roo.bootstrap.Element({
32481 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32484 var bottomText = new Roo.bootstrap.Element({
32486 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32489 topText.onRender(top.el, null);
32490 bottomText.onRender(bottom.el, null);
32493 item.bottomEl = bottom;
32496 this.barItems.push(item);
32501 getActive : function()
32503 var active = false;
32505 Roo.each(this.barItems, function(v){
32507 if (!v.isActive()) {
32519 setActiveItem : function(item)
32523 Roo.each(this.barItems, function(v){
32524 if (v.rid == item.rid) {
32528 if (v.isActive()) {
32529 v.setActive(false);
32534 item.setActive(true);
32536 this.fireEvent('changed', this, item, prev);
32539 getBarItem: function(rid)
32543 Roo.each(this.barItems, function(e) {
32544 if (e.rid != rid) {
32555 indexOfItem : function(item)
32559 Roo.each(this.barItems, function(v, i){
32561 if (v.rid != item.rid) {
32572 setActiveNext : function()
32574 var i = this.indexOfItem(this.getActive());
32576 if (i > this.barItems.length) {
32580 this.setActiveItem(this.barItems[i+1]);
32583 setActivePrev : function()
32585 var i = this.indexOfItem(this.getActive());
32591 this.setActiveItem(this.barItems[i-1]);
32594 format : function()
32596 if(!this.barItems.length){
32600 var width = 100 / this.barItems.length;
32602 Roo.each(this.barItems, function(i){
32603 i.el.setStyle('width', width + '%');
32604 i.topEl.el.setStyle('width', width + '%');
32605 i.bottomEl.el.setStyle('width', width + '%');
32614 * Nav Progress Item
32619 * @class Roo.bootstrap.NavProgressItem
32620 * @extends Roo.bootstrap.Component
32621 * Bootstrap NavProgressItem class
32622 * @cfg {String} rid the reference id
32623 * @cfg {Boolean} active (true|false) Is item active default false
32624 * @cfg {Boolean} disabled (true|false) Is item active default false
32625 * @cfg {String} html
32626 * @cfg {String} position (top|bottom) text position default bottom
32627 * @cfg {String} icon show icon instead of number
32630 * Create a new NavProgressItem
32631 * @param {Object} config The config object
32633 Roo.bootstrap.NavProgressItem = function(config){
32634 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32639 * The raw click event for the entire grid.
32640 * @param {Roo.bootstrap.NavProgressItem} this
32641 * @param {Roo.EventObject} e
32648 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32654 position : 'bottom',
32657 getAutoCreate : function()
32659 var iconCls = 'roo-navigation-bar-item-icon';
32661 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32665 cls: 'roo-navigation-bar-item',
32675 cfg.cls += ' active';
32678 cfg.cls += ' disabled';
32684 disable : function()
32686 this.setDisabled(true);
32689 enable : function()
32691 this.setDisabled(false);
32694 initEvents: function()
32696 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32698 this.iconEl.on('click', this.onClick, this);
32701 onClick : function(e)
32703 e.preventDefault();
32709 if(this.fireEvent('click', this, e) === false){
32713 this.parent().setActiveItem(this);
32716 isActive: function ()
32718 return this.active;
32721 setActive : function(state)
32723 if(this.active == state){
32727 this.active = state;
32730 this.el.addClass('active');
32734 this.el.removeClass('active');
32739 setDisabled : function(state)
32741 if(this.disabled == state){
32745 this.disabled = state;
32748 this.el.addClass('disabled');
32752 this.el.removeClass('disabled');
32755 tooltipEl : function()
32757 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32770 * @class Roo.bootstrap.FieldLabel
32771 * @extends Roo.bootstrap.Component
32772 * Bootstrap FieldLabel class
32773 * @cfg {String} html contents of the element
32774 * @cfg {String} tag tag of the element default label
32775 * @cfg {String} cls class of the element
32776 * @cfg {String} target label target
32777 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32778 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32779 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32780 * @cfg {String} iconTooltip default "This field is required"
32781 * @cfg {String} indicatorpos (left|right) default left
32784 * Create a new FieldLabel
32785 * @param {Object} config The config object
32788 Roo.bootstrap.FieldLabel = function(config){
32789 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32794 * Fires after the field has been marked as invalid.
32795 * @param {Roo.form.FieldLabel} this
32796 * @param {String} msg The validation message
32801 * Fires after the field has been validated with no errors.
32802 * @param {Roo.form.FieldLabel} this
32808 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32815 invalidClass : 'has-warning',
32816 validClass : 'has-success',
32817 iconTooltip : 'This field is required',
32818 indicatorpos : 'left',
32820 getAutoCreate : function(){
32823 if (!this.allowBlank) {
32829 cls : 'roo-bootstrap-field-label ' + this.cls,
32834 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32835 tooltip : this.iconTooltip
32844 if(this.indicatorpos == 'right'){
32847 cls : 'roo-bootstrap-field-label ' + this.cls,
32856 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32857 tooltip : this.iconTooltip
32866 initEvents: function()
32868 Roo.bootstrap.Element.superclass.initEvents.call(this);
32870 this.indicator = this.indicatorEl();
32872 if(this.indicator){
32873 this.indicator.removeClass('visible');
32874 this.indicator.addClass('invisible');
32877 Roo.bootstrap.FieldLabel.register(this);
32880 indicatorEl : function()
32882 var indicator = this.el.select('i.roo-required-indicator',true).first();
32893 * Mark this field as valid
32895 markValid : function()
32897 if(this.indicator){
32898 this.indicator.removeClass('visible');
32899 this.indicator.addClass('invisible');
32901 if (Roo.bootstrap.version == 3) {
32902 this.el.removeClass(this.invalidClass);
32903 this.el.addClass(this.validClass);
32905 this.el.removeClass('is-invalid');
32906 this.el.addClass('is-valid');
32910 this.fireEvent('valid', this);
32914 * Mark this field as invalid
32915 * @param {String} msg The validation message
32917 markInvalid : function(msg)
32919 if(this.indicator){
32920 this.indicator.removeClass('invisible');
32921 this.indicator.addClass('visible');
32923 if (Roo.bootstrap.version == 3) {
32924 this.el.removeClass(this.validClass);
32925 this.el.addClass(this.invalidClass);
32927 this.el.removeClass('is-valid');
32928 this.el.addClass('is-invalid');
32932 this.fireEvent('invalid', this, msg);
32938 Roo.apply(Roo.bootstrap.FieldLabel, {
32943 * register a FieldLabel Group
32944 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32946 register : function(label)
32948 if(this.groups.hasOwnProperty(label.target)){
32952 this.groups[label.target] = label;
32956 * fetch a FieldLabel Group based on the target
32957 * @param {string} target
32958 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32960 get: function(target) {
32961 if (typeof(this.groups[target]) == 'undefined') {
32965 return this.groups[target] ;
32974 * page DateSplitField.
32980 * @class Roo.bootstrap.DateSplitField
32981 * @extends Roo.bootstrap.Component
32982 * Bootstrap DateSplitField class
32983 * @cfg {string} fieldLabel - the label associated
32984 * @cfg {Number} labelWidth set the width of label (0-12)
32985 * @cfg {String} labelAlign (top|left)
32986 * @cfg {Boolean} dayAllowBlank (true|false) default false
32987 * @cfg {Boolean} monthAllowBlank (true|false) default false
32988 * @cfg {Boolean} yearAllowBlank (true|false) default false
32989 * @cfg {string} dayPlaceholder
32990 * @cfg {string} monthPlaceholder
32991 * @cfg {string} yearPlaceholder
32992 * @cfg {string} dayFormat default 'd'
32993 * @cfg {string} monthFormat default 'm'
32994 * @cfg {string} yearFormat default 'Y'
32995 * @cfg {Number} labellg set the width of label (1-12)
32996 * @cfg {Number} labelmd set the width of label (1-12)
32997 * @cfg {Number} labelsm set the width of label (1-12)
32998 * @cfg {Number} labelxs set the width of label (1-12)
33002 * Create a new DateSplitField
33003 * @param {Object} config The config object
33006 Roo.bootstrap.DateSplitField = function(config){
33007 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33013 * getting the data of years
33014 * @param {Roo.bootstrap.DateSplitField} this
33015 * @param {Object} years
33020 * getting the data of days
33021 * @param {Roo.bootstrap.DateSplitField} this
33022 * @param {Object} days
33027 * Fires after the field has been marked as invalid.
33028 * @param {Roo.form.Field} this
33029 * @param {String} msg The validation message
33034 * Fires after the field has been validated with no errors.
33035 * @param {Roo.form.Field} this
33041 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33044 labelAlign : 'top',
33046 dayAllowBlank : false,
33047 monthAllowBlank : false,
33048 yearAllowBlank : false,
33049 dayPlaceholder : '',
33050 monthPlaceholder : '',
33051 yearPlaceholder : '',
33055 isFormField : true,
33061 getAutoCreate : function()
33065 cls : 'row roo-date-split-field-group',
33070 cls : 'form-hidden-field roo-date-split-field-group-value',
33076 var labelCls = 'col-md-12';
33077 var contentCls = 'col-md-4';
33079 if(this.fieldLabel){
33083 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33087 html : this.fieldLabel
33092 if(this.labelAlign == 'left'){
33094 if(this.labelWidth > 12){
33095 label.style = "width: " + this.labelWidth + 'px';
33098 if(this.labelWidth < 13 && this.labelmd == 0){
33099 this.labelmd = this.labelWidth;
33102 if(this.labellg > 0){
33103 labelCls = ' col-lg-' + this.labellg;
33104 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33107 if(this.labelmd > 0){
33108 labelCls = ' col-md-' + this.labelmd;
33109 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33112 if(this.labelsm > 0){
33113 labelCls = ' col-sm-' + this.labelsm;
33114 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33117 if(this.labelxs > 0){
33118 labelCls = ' col-xs-' + this.labelxs;
33119 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33123 label.cls += ' ' + labelCls;
33125 cfg.cn.push(label);
33128 Roo.each(['day', 'month', 'year'], function(t){
33131 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33138 inputEl: function ()
33140 return this.el.select('.roo-date-split-field-group-value', true).first();
33143 onRender : function(ct, position)
33147 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33149 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33151 this.dayField = new Roo.bootstrap.ComboBox({
33152 allowBlank : this.dayAllowBlank,
33153 alwaysQuery : true,
33154 displayField : 'value',
33157 forceSelection : true,
33159 placeholder : this.dayPlaceholder,
33160 selectOnFocus : true,
33161 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33162 triggerAction : 'all',
33164 valueField : 'value',
33165 store : new Roo.data.SimpleStore({
33166 data : (function() {
33168 _this.fireEvent('days', _this, days);
33171 fields : [ 'value' ]
33174 select : function (_self, record, index)
33176 _this.setValue(_this.getValue());
33181 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33183 this.monthField = new Roo.bootstrap.MonthField({
33184 after : '<i class=\"fa fa-calendar\"></i>',
33185 allowBlank : this.monthAllowBlank,
33186 placeholder : this.monthPlaceholder,
33189 render : function (_self)
33191 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33192 e.preventDefault();
33196 select : function (_self, oldvalue, newvalue)
33198 _this.setValue(_this.getValue());
33203 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33205 this.yearField = new Roo.bootstrap.ComboBox({
33206 allowBlank : this.yearAllowBlank,
33207 alwaysQuery : true,
33208 displayField : 'value',
33211 forceSelection : true,
33213 placeholder : this.yearPlaceholder,
33214 selectOnFocus : true,
33215 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33216 triggerAction : 'all',
33218 valueField : 'value',
33219 store : new Roo.data.SimpleStore({
33220 data : (function() {
33222 _this.fireEvent('years', _this, years);
33225 fields : [ 'value' ]
33228 select : function (_self, record, index)
33230 _this.setValue(_this.getValue());
33235 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33238 setValue : function(v, format)
33240 this.inputEl.dom.value = v;
33242 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33244 var d = Date.parseDate(v, f);
33251 this.setDay(d.format(this.dayFormat));
33252 this.setMonth(d.format(this.monthFormat));
33253 this.setYear(d.format(this.yearFormat));
33260 setDay : function(v)
33262 this.dayField.setValue(v);
33263 this.inputEl.dom.value = this.getValue();
33268 setMonth : function(v)
33270 this.monthField.setValue(v, true);
33271 this.inputEl.dom.value = this.getValue();
33276 setYear : function(v)
33278 this.yearField.setValue(v);
33279 this.inputEl.dom.value = this.getValue();
33284 getDay : function()
33286 return this.dayField.getValue();
33289 getMonth : function()
33291 return this.monthField.getValue();
33294 getYear : function()
33296 return this.yearField.getValue();
33299 getValue : function()
33301 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33303 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33313 this.inputEl.dom.value = '';
33318 validate : function()
33320 var d = this.dayField.validate();
33321 var m = this.monthField.validate();
33322 var y = this.yearField.validate();
33327 (!this.dayAllowBlank && !d) ||
33328 (!this.monthAllowBlank && !m) ||
33329 (!this.yearAllowBlank && !y)
33334 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33343 this.markInvalid();
33348 markValid : function()
33351 var label = this.el.select('label', true).first();
33352 var icon = this.el.select('i.fa-star', true).first();
33358 this.fireEvent('valid', this);
33362 * Mark this field as invalid
33363 * @param {String} msg The validation message
33365 markInvalid : function(msg)
33368 var label = this.el.select('label', true).first();
33369 var icon = this.el.select('i.fa-star', true).first();
33371 if(label && !icon){
33372 this.el.select('.roo-date-split-field-label', true).createChild({
33374 cls : 'text-danger fa fa-lg fa-star',
33375 tooltip : 'This field is required',
33376 style : 'margin-right:5px;'
33380 this.fireEvent('invalid', this, msg);
33383 clearInvalid : function()
33385 var label = this.el.select('label', true).first();
33386 var icon = this.el.select('i.fa-star', true).first();
33392 this.fireEvent('valid', this);
33395 getName: function()
33405 * http://masonry.desandro.com
33407 * The idea is to render all the bricks based on vertical width...
33409 * The original code extends 'outlayer' - we might need to use that....
33415 * @class Roo.bootstrap.LayoutMasonry
33416 * @extends Roo.bootstrap.Component
33417 * Bootstrap Layout Masonry class
33420 * Create a new Element
33421 * @param {Object} config The config object
33424 Roo.bootstrap.LayoutMasonry = function(config){
33426 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33430 Roo.bootstrap.LayoutMasonry.register(this);
33436 * Fire after layout the items
33437 * @param {Roo.bootstrap.LayoutMasonry} this
33438 * @param {Roo.EventObject} e
33445 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33448 * @cfg {Boolean} isLayoutInstant = no animation?
33450 isLayoutInstant : false, // needed?
33453 * @cfg {Number} boxWidth width of the columns
33458 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33463 * @cfg {Number} padWidth padding below box..
33468 * @cfg {Number} gutter gutter width..
33473 * @cfg {Number} maxCols maximum number of columns
33479 * @cfg {Boolean} isAutoInitial defalut true
33481 isAutoInitial : true,
33486 * @cfg {Boolean} isHorizontal defalut false
33488 isHorizontal : false,
33490 currentSize : null,
33496 bricks: null, //CompositeElement
33500 _isLayoutInited : false,
33502 // isAlternative : false, // only use for vertical layout...
33505 * @cfg {Number} alternativePadWidth padding below box..
33507 alternativePadWidth : 50,
33509 selectedBrick : [],
33511 getAutoCreate : function(){
33513 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33517 cls: 'blog-masonary-wrapper ' + this.cls,
33519 cls : 'mas-boxes masonary'
33526 getChildContainer: function( )
33528 if (this.boxesEl) {
33529 return this.boxesEl;
33532 this.boxesEl = this.el.select('.mas-boxes').first();
33534 return this.boxesEl;
33538 initEvents : function()
33542 if(this.isAutoInitial){
33543 Roo.log('hook children rendered');
33544 this.on('childrenrendered', function() {
33545 Roo.log('children rendered');
33551 initial : function()
33553 this.selectedBrick = [];
33555 this.currentSize = this.el.getBox(true);
33557 Roo.EventManager.onWindowResize(this.resize, this);
33559 if(!this.isAutoInitial){
33567 //this.layout.defer(500,this);
33571 resize : function()
33573 var cs = this.el.getBox(true);
33576 this.currentSize.width == cs.width &&
33577 this.currentSize.x == cs.x &&
33578 this.currentSize.height == cs.height &&
33579 this.currentSize.y == cs.y
33581 Roo.log("no change in with or X or Y");
33585 this.currentSize = cs;
33591 layout : function()
33593 this._resetLayout();
33595 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33597 this.layoutItems( isInstant );
33599 this._isLayoutInited = true;
33601 this.fireEvent('layout', this);
33605 _resetLayout : function()
33607 if(this.isHorizontal){
33608 this.horizontalMeasureColumns();
33612 this.verticalMeasureColumns();
33616 verticalMeasureColumns : function()
33618 this.getContainerWidth();
33620 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33621 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33625 var boxWidth = this.boxWidth + this.padWidth;
33627 if(this.containerWidth < this.boxWidth){
33628 boxWidth = this.containerWidth
33631 var containerWidth = this.containerWidth;
33633 var cols = Math.floor(containerWidth / boxWidth);
33635 this.cols = Math.max( cols, 1 );
33637 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33639 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33641 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33643 this.colWidth = boxWidth + avail - this.padWidth;
33645 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33646 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33649 horizontalMeasureColumns : function()
33651 this.getContainerWidth();
33653 var boxWidth = this.boxWidth;
33655 if(this.containerWidth < boxWidth){
33656 boxWidth = this.containerWidth;
33659 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33661 this.el.setHeight(boxWidth);
33665 getContainerWidth : function()
33667 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33670 layoutItems : function( isInstant )
33672 Roo.log(this.bricks);
33674 var items = Roo.apply([], this.bricks);
33676 if(this.isHorizontal){
33677 this._horizontalLayoutItems( items , isInstant );
33681 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33682 // this._verticalAlternativeLayoutItems( items , isInstant );
33686 this._verticalLayoutItems( items , isInstant );
33690 _verticalLayoutItems : function ( items , isInstant)
33692 if ( !items || !items.length ) {
33697 ['xs', 'xs', 'xs', 'tall'],
33698 ['xs', 'xs', 'tall'],
33699 ['xs', 'xs', 'sm'],
33700 ['xs', 'xs', 'xs'],
33706 ['sm', 'xs', 'xs'],
33710 ['tall', 'xs', 'xs', 'xs'],
33711 ['tall', 'xs', 'xs'],
33723 Roo.each(items, function(item, k){
33725 switch (item.size) {
33726 // these layouts take up a full box,
33737 boxes.push([item]);
33760 var filterPattern = function(box, length)
33768 var pattern = box.slice(0, length);
33772 Roo.each(pattern, function(i){
33773 format.push(i.size);
33776 Roo.each(standard, function(s){
33778 if(String(s) != String(format)){
33787 if(!match && length == 1){
33792 filterPattern(box, length - 1);
33796 queue.push(pattern);
33798 box = box.slice(length, box.length);
33800 filterPattern(box, 4);
33806 Roo.each(boxes, function(box, k){
33812 if(box.length == 1){
33817 filterPattern(box, 4);
33821 this._processVerticalLayoutQueue( queue, isInstant );
33825 // _verticalAlternativeLayoutItems : function( items , isInstant )
33827 // if ( !items || !items.length ) {
33831 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33835 _horizontalLayoutItems : function ( items , isInstant)
33837 if ( !items || !items.length || items.length < 3) {
33843 var eItems = items.slice(0, 3);
33845 items = items.slice(3, items.length);
33848 ['xs', 'xs', 'xs', 'wide'],
33849 ['xs', 'xs', 'wide'],
33850 ['xs', 'xs', 'sm'],
33851 ['xs', 'xs', 'xs'],
33857 ['sm', 'xs', 'xs'],
33861 ['wide', 'xs', 'xs', 'xs'],
33862 ['wide', 'xs', 'xs'],
33875 Roo.each(items, function(item, k){
33877 switch (item.size) {
33888 boxes.push([item]);
33912 var filterPattern = function(box, length)
33920 var pattern = box.slice(0, length);
33924 Roo.each(pattern, function(i){
33925 format.push(i.size);
33928 Roo.each(standard, function(s){
33930 if(String(s) != String(format)){
33939 if(!match && length == 1){
33944 filterPattern(box, length - 1);
33948 queue.push(pattern);
33950 box = box.slice(length, box.length);
33952 filterPattern(box, 4);
33958 Roo.each(boxes, function(box, k){
33964 if(box.length == 1){
33969 filterPattern(box, 4);
33976 var pos = this.el.getBox(true);
33980 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33982 var hit_end = false;
33984 Roo.each(queue, function(box){
33988 Roo.each(box, function(b){
33990 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34000 Roo.each(box, function(b){
34002 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34005 mx = Math.max(mx, b.x);
34009 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34013 Roo.each(box, function(b){
34015 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34029 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34032 /** Sets position of item in DOM
34033 * @param {Element} item
34034 * @param {Number} x - horizontal position
34035 * @param {Number} y - vertical position
34036 * @param {Boolean} isInstant - disables transitions
34038 _processVerticalLayoutQueue : function( queue, isInstant )
34040 var pos = this.el.getBox(true);
34045 for (var i = 0; i < this.cols; i++){
34049 Roo.each(queue, function(box, k){
34051 var col = k % this.cols;
34053 Roo.each(box, function(b,kk){
34055 b.el.position('absolute');
34057 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34058 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34060 if(b.size == 'md-left' || b.size == 'md-right'){
34061 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34062 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34065 b.el.setWidth(width);
34066 b.el.setHeight(height);
34068 b.el.select('iframe',true).setSize(width,height);
34072 for (var i = 0; i < this.cols; i++){
34074 if(maxY[i] < maxY[col]){
34079 col = Math.min(col, i);
34083 x = pos.x + col * (this.colWidth + this.padWidth);
34087 var positions = [];
34089 switch (box.length){
34091 positions = this.getVerticalOneBoxColPositions(x, y, box);
34094 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34097 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34100 positions = this.getVerticalFourBoxColPositions(x, y, box);
34106 Roo.each(box, function(b,kk){
34108 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34110 var sz = b.el.getSize();
34112 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34120 for (var i = 0; i < this.cols; i++){
34121 mY = Math.max(mY, maxY[i]);
34124 this.el.setHeight(mY - pos.y);
34128 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34130 // var pos = this.el.getBox(true);
34133 // var maxX = pos.right;
34135 // var maxHeight = 0;
34137 // Roo.each(items, function(item, k){
34141 // item.el.position('absolute');
34143 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34145 // item.el.setWidth(width);
34147 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34149 // item.el.setHeight(height);
34152 // item.el.setXY([x, y], isInstant ? false : true);
34154 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34157 // y = y + height + this.alternativePadWidth;
34159 // maxHeight = maxHeight + height + this.alternativePadWidth;
34163 // this.el.setHeight(maxHeight);
34167 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34169 var pos = this.el.getBox(true);
34174 var maxX = pos.right;
34176 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34178 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34180 Roo.each(queue, function(box, k){
34182 Roo.each(box, function(b, kk){
34184 b.el.position('absolute');
34186 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34187 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34189 if(b.size == 'md-left' || b.size == 'md-right'){
34190 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34191 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34194 b.el.setWidth(width);
34195 b.el.setHeight(height);
34203 var positions = [];
34205 switch (box.length){
34207 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34210 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34213 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34216 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34222 Roo.each(box, function(b,kk){
34224 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34226 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34234 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34236 Roo.each(eItems, function(b,k){
34238 b.size = (k == 0) ? 'sm' : 'xs';
34239 b.x = (k == 0) ? 2 : 1;
34240 b.y = (k == 0) ? 2 : 1;
34242 b.el.position('absolute');
34244 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34246 b.el.setWidth(width);
34248 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34250 b.el.setHeight(height);
34254 var positions = [];
34257 x : maxX - this.unitWidth * 2 - this.gutter,
34262 x : maxX - this.unitWidth,
34263 y : minY + (this.unitWidth + this.gutter) * 2
34267 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34271 Roo.each(eItems, function(b,k){
34273 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34279 getVerticalOneBoxColPositions : function(x, y, box)
34283 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34285 if(box[0].size == 'md-left'){
34289 if(box[0].size == 'md-right'){
34294 x : x + (this.unitWidth + this.gutter) * rand,
34301 getVerticalTwoBoxColPositions : function(x, y, box)
34305 if(box[0].size == 'xs'){
34309 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34313 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34327 x : x + (this.unitWidth + this.gutter) * 2,
34328 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34335 getVerticalThreeBoxColPositions : function(x, y, box)
34339 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34347 x : x + (this.unitWidth + this.gutter) * 1,
34352 x : x + (this.unitWidth + this.gutter) * 2,
34360 if(box[0].size == 'xs' && box[1].size == 'xs'){
34369 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34373 x : x + (this.unitWidth + this.gutter) * 1,
34387 x : x + (this.unitWidth + this.gutter) * 2,
34392 x : x + (this.unitWidth + this.gutter) * 2,
34393 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34400 getVerticalFourBoxColPositions : function(x, y, box)
34404 if(box[0].size == 'xs'){
34413 y : y + (this.unitHeight + this.gutter) * 1
34418 y : y + (this.unitHeight + this.gutter) * 2
34422 x : x + (this.unitWidth + this.gutter) * 1,
34436 x : x + (this.unitWidth + this.gutter) * 2,
34441 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34442 y : y + (this.unitHeight + this.gutter) * 1
34446 x : x + (this.unitWidth + this.gutter) * 2,
34447 y : y + (this.unitWidth + this.gutter) * 2
34454 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34458 if(box[0].size == 'md-left'){
34460 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34467 if(box[0].size == 'md-right'){
34469 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34470 y : minY + (this.unitWidth + this.gutter) * 1
34476 var rand = Math.floor(Math.random() * (4 - box[0].y));
34479 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34480 y : minY + (this.unitWidth + this.gutter) * rand
34487 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34491 if(box[0].size == 'xs'){
34494 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34499 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34500 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34508 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34513 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34514 y : minY + (this.unitWidth + this.gutter) * 2
34521 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34525 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34528 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34533 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34534 y : minY + (this.unitWidth + this.gutter) * 1
34538 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34539 y : minY + (this.unitWidth + this.gutter) * 2
34546 if(box[0].size == 'xs' && box[1].size == 'xs'){
34549 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34554 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34559 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34560 y : minY + (this.unitWidth + this.gutter) * 1
34568 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34573 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34574 y : minY + (this.unitWidth + this.gutter) * 2
34578 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34579 y : minY + (this.unitWidth + this.gutter) * 2
34586 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34590 if(box[0].size == 'xs'){
34593 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34598 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34603 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),
34608 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34609 y : minY + (this.unitWidth + this.gutter) * 1
34617 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34622 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34623 y : minY + (this.unitWidth + this.gutter) * 2
34627 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34628 y : minY + (this.unitWidth + this.gutter) * 2
34632 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),
34633 y : minY + (this.unitWidth + this.gutter) * 2
34641 * remove a Masonry Brick
34642 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34644 removeBrick : function(brick_id)
34650 for (var i = 0; i<this.bricks.length; i++) {
34651 if (this.bricks[i].id == brick_id) {
34652 this.bricks.splice(i,1);
34653 this.el.dom.removeChild(Roo.get(brick_id).dom);
34660 * adds a Masonry Brick
34661 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34663 addBrick : function(cfg)
34665 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34666 //this.register(cn);
34667 cn.parentId = this.id;
34668 cn.render(this.el);
34673 * register a Masonry Brick
34674 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34677 register : function(brick)
34679 this.bricks.push(brick);
34680 brick.masonryId = this.id;
34684 * clear all the Masonry Brick
34686 clearAll : function()
34689 //this.getChildContainer().dom.innerHTML = "";
34690 this.el.dom.innerHTML = '';
34693 getSelected : function()
34695 if (!this.selectedBrick) {
34699 return this.selectedBrick;
34703 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34707 * register a Masonry Layout
34708 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34711 register : function(layout)
34713 this.groups[layout.id] = layout;
34716 * fetch a Masonry Layout based on the masonry layout ID
34717 * @param {string} the masonry layout to add
34718 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34721 get: function(layout_id) {
34722 if (typeof(this.groups[layout_id]) == 'undefined') {
34725 return this.groups[layout_id] ;
34737 * http://masonry.desandro.com
34739 * The idea is to render all the bricks based on vertical width...
34741 * The original code extends 'outlayer' - we might need to use that....
34747 * @class Roo.bootstrap.LayoutMasonryAuto
34748 * @extends Roo.bootstrap.Component
34749 * Bootstrap Layout Masonry class
34752 * Create a new Element
34753 * @param {Object} config The config object
34756 Roo.bootstrap.LayoutMasonryAuto = function(config){
34757 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34760 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34763 * @cfg {Boolean} isFitWidth - resize the width..
34765 isFitWidth : false, // options..
34767 * @cfg {Boolean} isOriginLeft = left align?
34769 isOriginLeft : true,
34771 * @cfg {Boolean} isOriginTop = top align?
34773 isOriginTop : false,
34775 * @cfg {Boolean} isLayoutInstant = no animation?
34777 isLayoutInstant : false, // needed?
34779 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34781 isResizingContainer : true,
34783 * @cfg {Number} columnWidth width of the columns
34789 * @cfg {Number} maxCols maximum number of columns
34794 * @cfg {Number} padHeight padding below box..
34800 * @cfg {Boolean} isAutoInitial defalut true
34803 isAutoInitial : true,
34809 initialColumnWidth : 0,
34810 currentSize : null,
34812 colYs : null, // array.
34819 bricks: null, //CompositeElement
34820 cols : 0, // array?
34821 // element : null, // wrapped now this.el
34822 _isLayoutInited : null,
34825 getAutoCreate : function(){
34829 cls: 'blog-masonary-wrapper ' + this.cls,
34831 cls : 'mas-boxes masonary'
34838 getChildContainer: function( )
34840 if (this.boxesEl) {
34841 return this.boxesEl;
34844 this.boxesEl = this.el.select('.mas-boxes').first();
34846 return this.boxesEl;
34850 initEvents : function()
34854 if(this.isAutoInitial){
34855 Roo.log('hook children rendered');
34856 this.on('childrenrendered', function() {
34857 Roo.log('children rendered');
34864 initial : function()
34866 this.reloadItems();
34868 this.currentSize = this.el.getBox(true);
34870 /// was window resize... - let's see if this works..
34871 Roo.EventManager.onWindowResize(this.resize, this);
34873 if(!this.isAutoInitial){
34878 this.layout.defer(500,this);
34881 reloadItems: function()
34883 this.bricks = this.el.select('.masonry-brick', true);
34885 this.bricks.each(function(b) {
34886 //Roo.log(b.getSize());
34887 if (!b.attr('originalwidth')) {
34888 b.attr('originalwidth', b.getSize().width);
34893 Roo.log(this.bricks.elements.length);
34896 resize : function()
34899 var cs = this.el.getBox(true);
34901 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34902 Roo.log("no change in with or X");
34905 this.currentSize = cs;
34909 layout : function()
34912 this._resetLayout();
34913 //this._manageStamps();
34915 // don't animate first layout
34916 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34917 this.layoutItems( isInstant );
34919 // flag for initalized
34920 this._isLayoutInited = true;
34923 layoutItems : function( isInstant )
34925 //var items = this._getItemsForLayout( this.items );
34926 // original code supports filtering layout items.. we just ignore it..
34928 this._layoutItems( this.bricks , isInstant );
34930 this._postLayout();
34932 _layoutItems : function ( items , isInstant)
34934 //this.fireEvent( 'layout', this, items );
34937 if ( !items || !items.elements.length ) {
34938 // no items, emit event with empty array
34943 items.each(function(item) {
34944 Roo.log("layout item");
34946 // get x/y object from method
34947 var position = this._getItemLayoutPosition( item );
34949 position.item = item;
34950 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34951 queue.push( position );
34954 this._processLayoutQueue( queue );
34956 /** Sets position of item in DOM
34957 * @param {Element} item
34958 * @param {Number} x - horizontal position
34959 * @param {Number} y - vertical position
34960 * @param {Boolean} isInstant - disables transitions
34962 _processLayoutQueue : function( queue )
34964 for ( var i=0, len = queue.length; i < len; i++ ) {
34965 var obj = queue[i];
34966 obj.item.position('absolute');
34967 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34973 * Any logic you want to do after each layout,
34974 * i.e. size the container
34976 _postLayout : function()
34978 this.resizeContainer();
34981 resizeContainer : function()
34983 if ( !this.isResizingContainer ) {
34986 var size = this._getContainerSize();
34988 this.el.setSize(size.width,size.height);
34989 this.boxesEl.setSize(size.width,size.height);
34995 _resetLayout : function()
34997 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34998 this.colWidth = this.el.getWidth();
34999 //this.gutter = this.el.getWidth();
35001 this.measureColumns();
35007 this.colYs.push( 0 );
35013 measureColumns : function()
35015 this.getContainerWidth();
35016 // if columnWidth is 0, default to outerWidth of first item
35017 if ( !this.columnWidth ) {
35018 var firstItem = this.bricks.first();
35019 Roo.log(firstItem);
35020 this.columnWidth = this.containerWidth;
35021 if (firstItem && firstItem.attr('originalwidth') ) {
35022 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35024 // columnWidth fall back to item of first element
35025 Roo.log("set column width?");
35026 this.initialColumnWidth = this.columnWidth ;
35028 // if first elem has no width, default to size of container
35033 if (this.initialColumnWidth) {
35034 this.columnWidth = this.initialColumnWidth;
35039 // column width is fixed at the top - however if container width get's smaller we should
35042 // this bit calcs how man columns..
35044 var columnWidth = this.columnWidth += this.gutter;
35046 // calculate columns
35047 var containerWidth = this.containerWidth + this.gutter;
35049 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35050 // fix rounding errors, typically with gutters
35051 var excess = columnWidth - containerWidth % columnWidth;
35054 // if overshoot is less than a pixel, round up, otherwise floor it
35055 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35056 cols = Math[ mathMethod ]( cols );
35057 this.cols = Math.max( cols, 1 );
35058 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35060 // padding positioning..
35061 var totalColWidth = this.cols * this.columnWidth;
35062 var padavail = this.containerWidth - totalColWidth;
35063 // so for 2 columns - we need 3 'pads'
35065 var padNeeded = (1+this.cols) * this.padWidth;
35067 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35069 this.columnWidth += padExtra
35070 //this.padWidth = Math.floor(padavail / ( this.cols));
35072 // adjust colum width so that padding is fixed??
35074 // we have 3 columns ... total = width * 3
35075 // we have X left over... that should be used by
35077 //if (this.expandC) {
35085 getContainerWidth : function()
35087 /* // container is parent if fit width
35088 var container = this.isFitWidth ? this.element.parentNode : this.element;
35089 // check that this.size and size are there
35090 // IE8 triggers resize on body size change, so they might not be
35092 var size = getSize( container ); //FIXME
35093 this.containerWidth = size && size.innerWidth; //FIXME
35096 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35100 _getItemLayoutPosition : function( item ) // what is item?
35102 // we resize the item to our columnWidth..
35104 item.setWidth(this.columnWidth);
35105 item.autoBoxAdjust = false;
35107 var sz = item.getSize();
35109 // how many columns does this brick span
35110 var remainder = this.containerWidth % this.columnWidth;
35112 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35113 // round if off by 1 pixel, otherwise use ceil
35114 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35115 colSpan = Math.min( colSpan, this.cols );
35117 // normally this should be '1' as we dont' currently allow multi width columns..
35119 var colGroup = this._getColGroup( colSpan );
35120 // get the minimum Y value from the columns
35121 var minimumY = Math.min.apply( Math, colGroup );
35122 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35124 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35126 // position the brick
35128 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35129 y: this.currentSize.y + minimumY + this.padHeight
35133 // apply setHeight to necessary columns
35134 var setHeight = minimumY + sz.height + this.padHeight;
35135 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35137 var setSpan = this.cols + 1 - colGroup.length;
35138 for ( var i = 0; i < setSpan; i++ ) {
35139 this.colYs[ shortColIndex + i ] = setHeight ;
35146 * @param {Number} colSpan - number of columns the element spans
35147 * @returns {Array} colGroup
35149 _getColGroup : function( colSpan )
35151 if ( colSpan < 2 ) {
35152 // if brick spans only one column, use all the column Ys
35157 // how many different places could this brick fit horizontally
35158 var groupCount = this.cols + 1 - colSpan;
35159 // for each group potential horizontal position
35160 for ( var i = 0; i < groupCount; i++ ) {
35161 // make an array of colY values for that one group
35162 var groupColYs = this.colYs.slice( i, i + colSpan );
35163 // and get the max value of the array
35164 colGroup[i] = Math.max.apply( Math, groupColYs );
35169 _manageStamp : function( stamp )
35171 var stampSize = stamp.getSize();
35172 var offset = stamp.getBox();
35173 // get the columns that this stamp affects
35174 var firstX = this.isOriginLeft ? offset.x : offset.right;
35175 var lastX = firstX + stampSize.width;
35176 var firstCol = Math.floor( firstX / this.columnWidth );
35177 firstCol = Math.max( 0, firstCol );
35179 var lastCol = Math.floor( lastX / this.columnWidth );
35180 // lastCol should not go over if multiple of columnWidth #425
35181 lastCol -= lastX % this.columnWidth ? 0 : 1;
35182 lastCol = Math.min( this.cols - 1, lastCol );
35184 // set colYs to bottom of the stamp
35185 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35188 for ( var i = firstCol; i <= lastCol; i++ ) {
35189 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35194 _getContainerSize : function()
35196 this.maxY = Math.max.apply( Math, this.colYs );
35201 if ( this.isFitWidth ) {
35202 size.width = this._getContainerFitWidth();
35208 _getContainerFitWidth : function()
35210 var unusedCols = 0;
35211 // count unused columns
35214 if ( this.colYs[i] !== 0 ) {
35219 // fit container to columns that have been used
35220 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35223 needsResizeLayout : function()
35225 var previousWidth = this.containerWidth;
35226 this.getContainerWidth();
35227 return previousWidth !== this.containerWidth;
35242 * @class Roo.bootstrap.MasonryBrick
35243 * @extends Roo.bootstrap.Component
35244 * Bootstrap MasonryBrick class
35247 * Create a new MasonryBrick
35248 * @param {Object} config The config object
35251 Roo.bootstrap.MasonryBrick = function(config){
35253 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35255 Roo.bootstrap.MasonryBrick.register(this);
35261 * When a MasonryBrick is clcik
35262 * @param {Roo.bootstrap.MasonryBrick} this
35263 * @param {Roo.EventObject} e
35269 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35272 * @cfg {String} title
35276 * @cfg {String} html
35280 * @cfg {String} bgimage
35284 * @cfg {String} videourl
35288 * @cfg {String} cls
35292 * @cfg {String} href
35296 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35301 * @cfg {String} placetitle (center|bottom)
35306 * @cfg {Boolean} isFitContainer defalut true
35308 isFitContainer : true,
35311 * @cfg {Boolean} preventDefault defalut false
35313 preventDefault : false,
35316 * @cfg {Boolean} inverse defalut false
35318 maskInverse : false,
35320 getAutoCreate : function()
35322 if(!this.isFitContainer){
35323 return this.getSplitAutoCreate();
35326 var cls = 'masonry-brick masonry-brick-full';
35328 if(this.href.length){
35329 cls += ' masonry-brick-link';
35332 if(this.bgimage.length){
35333 cls += ' masonry-brick-image';
35336 if(this.maskInverse){
35337 cls += ' mask-inverse';
35340 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35341 cls += ' enable-mask';
35345 cls += ' masonry-' + this.size + '-brick';
35348 if(this.placetitle.length){
35350 switch (this.placetitle) {
35352 cls += ' masonry-center-title';
35355 cls += ' masonry-bottom-title';
35362 if(!this.html.length && !this.bgimage.length){
35363 cls += ' masonry-center-title';
35366 if(!this.html.length && this.bgimage.length){
35367 cls += ' masonry-bottom-title';
35372 cls += ' ' + this.cls;
35376 tag: (this.href.length) ? 'a' : 'div',
35381 cls: 'masonry-brick-mask'
35385 cls: 'masonry-brick-paragraph',
35391 if(this.href.length){
35392 cfg.href = this.href;
35395 var cn = cfg.cn[1].cn;
35397 if(this.title.length){
35400 cls: 'masonry-brick-title',
35405 if(this.html.length){
35408 cls: 'masonry-brick-text',
35413 if (!this.title.length && !this.html.length) {
35414 cfg.cn[1].cls += ' hide';
35417 if(this.bgimage.length){
35420 cls: 'masonry-brick-image-view',
35425 if(this.videourl.length){
35426 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35427 // youtube support only?
35430 cls: 'masonry-brick-image-view',
35433 allowfullscreen : true
35441 getSplitAutoCreate : function()
35443 var cls = 'masonry-brick masonry-brick-split';
35445 if(this.href.length){
35446 cls += ' masonry-brick-link';
35449 if(this.bgimage.length){
35450 cls += ' masonry-brick-image';
35454 cls += ' masonry-' + this.size + '-brick';
35457 switch (this.placetitle) {
35459 cls += ' masonry-center-title';
35462 cls += ' masonry-bottom-title';
35465 if(!this.bgimage.length){
35466 cls += ' masonry-center-title';
35469 if(this.bgimage.length){
35470 cls += ' masonry-bottom-title';
35476 cls += ' ' + this.cls;
35480 tag: (this.href.length) ? 'a' : 'div',
35485 cls: 'masonry-brick-split-head',
35489 cls: 'masonry-brick-paragraph',
35496 cls: 'masonry-brick-split-body',
35502 if(this.href.length){
35503 cfg.href = this.href;
35506 if(this.title.length){
35507 cfg.cn[0].cn[0].cn.push({
35509 cls: 'masonry-brick-title',
35514 if(this.html.length){
35515 cfg.cn[1].cn.push({
35517 cls: 'masonry-brick-text',
35522 if(this.bgimage.length){
35523 cfg.cn[0].cn.push({
35525 cls: 'masonry-brick-image-view',
35530 if(this.videourl.length){
35531 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35532 // youtube support only?
35533 cfg.cn[0].cn.cn.push({
35535 cls: 'masonry-brick-image-view',
35538 allowfullscreen : true
35545 initEvents: function()
35547 switch (this.size) {
35580 this.el.on('touchstart', this.onTouchStart, this);
35581 this.el.on('touchmove', this.onTouchMove, this);
35582 this.el.on('touchend', this.onTouchEnd, this);
35583 this.el.on('contextmenu', this.onContextMenu, this);
35585 this.el.on('mouseenter' ,this.enter, this);
35586 this.el.on('mouseleave', this.leave, this);
35587 this.el.on('click', this.onClick, this);
35590 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35591 this.parent().bricks.push(this);
35596 onClick: function(e, el)
35598 var time = this.endTimer - this.startTimer;
35599 // Roo.log(e.preventDefault());
35602 e.preventDefault();
35607 if(!this.preventDefault){
35611 e.preventDefault();
35613 if (this.activeClass != '') {
35614 this.selectBrick();
35617 this.fireEvent('click', this, e);
35620 enter: function(e, el)
35622 e.preventDefault();
35624 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35628 if(this.bgimage.length && this.html.length){
35629 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35633 leave: function(e, el)
35635 e.preventDefault();
35637 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35641 if(this.bgimage.length && this.html.length){
35642 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35646 onTouchStart: function(e, el)
35648 // e.preventDefault();
35650 this.touchmoved = false;
35652 if(!this.isFitContainer){
35656 if(!this.bgimage.length || !this.html.length){
35660 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35662 this.timer = new Date().getTime();
35666 onTouchMove: function(e, el)
35668 this.touchmoved = true;
35671 onContextMenu : function(e,el)
35673 e.preventDefault();
35674 e.stopPropagation();
35678 onTouchEnd: function(e, el)
35680 // e.preventDefault();
35682 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35689 if(!this.bgimage.length || !this.html.length){
35691 if(this.href.length){
35692 window.location.href = this.href;
35698 if(!this.isFitContainer){
35702 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35704 window.location.href = this.href;
35707 //selection on single brick only
35708 selectBrick : function() {
35710 if (!this.parentId) {
35714 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35715 var index = m.selectedBrick.indexOf(this.id);
35718 m.selectedBrick.splice(index,1);
35719 this.el.removeClass(this.activeClass);
35723 for(var i = 0; i < m.selectedBrick.length; i++) {
35724 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35725 b.el.removeClass(b.activeClass);
35728 m.selectedBrick = [];
35730 m.selectedBrick.push(this.id);
35731 this.el.addClass(this.activeClass);
35735 isSelected : function(){
35736 return this.el.hasClass(this.activeClass);
35741 Roo.apply(Roo.bootstrap.MasonryBrick, {
35744 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35746 * register a Masonry Brick
35747 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35750 register : function(brick)
35752 //this.groups[brick.id] = brick;
35753 this.groups.add(brick.id, brick);
35756 * fetch a masonry brick based on the masonry brick ID
35757 * @param {string} the masonry brick to add
35758 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35761 get: function(brick_id)
35763 // if (typeof(this.groups[brick_id]) == 'undefined') {
35766 // return this.groups[brick_id] ;
35768 if(this.groups.key(brick_id)) {
35769 return this.groups.key(brick_id);
35787 * @class Roo.bootstrap.Brick
35788 * @extends Roo.bootstrap.Component
35789 * Bootstrap Brick class
35792 * Create a new Brick
35793 * @param {Object} config The config object
35796 Roo.bootstrap.Brick = function(config){
35797 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35803 * When a Brick is click
35804 * @param {Roo.bootstrap.Brick} this
35805 * @param {Roo.EventObject} e
35811 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35814 * @cfg {String} title
35818 * @cfg {String} html
35822 * @cfg {String} bgimage
35826 * @cfg {String} cls
35830 * @cfg {String} href
35834 * @cfg {String} video
35838 * @cfg {Boolean} square
35842 getAutoCreate : function()
35844 var cls = 'roo-brick';
35846 if(this.href.length){
35847 cls += ' roo-brick-link';
35850 if(this.bgimage.length){
35851 cls += ' roo-brick-image';
35854 if(!this.html.length && !this.bgimage.length){
35855 cls += ' roo-brick-center-title';
35858 if(!this.html.length && this.bgimage.length){
35859 cls += ' roo-brick-bottom-title';
35863 cls += ' ' + this.cls;
35867 tag: (this.href.length) ? 'a' : 'div',
35872 cls: 'roo-brick-paragraph',
35878 if(this.href.length){
35879 cfg.href = this.href;
35882 var cn = cfg.cn[0].cn;
35884 if(this.title.length){
35887 cls: 'roo-brick-title',
35892 if(this.html.length){
35895 cls: 'roo-brick-text',
35902 if(this.bgimage.length){
35905 cls: 'roo-brick-image-view',
35913 initEvents: function()
35915 if(this.title.length || this.html.length){
35916 this.el.on('mouseenter' ,this.enter, this);
35917 this.el.on('mouseleave', this.leave, this);
35920 Roo.EventManager.onWindowResize(this.resize, this);
35922 if(this.bgimage.length){
35923 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35924 this.imageEl.on('load', this.onImageLoad, this);
35931 onImageLoad : function()
35936 resize : function()
35938 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35940 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35942 if(this.bgimage.length){
35943 var image = this.el.select('.roo-brick-image-view', true).first();
35945 image.setWidth(paragraph.getWidth());
35948 image.setHeight(paragraph.getWidth());
35951 this.el.setHeight(image.getHeight());
35952 paragraph.setHeight(image.getHeight());
35958 enter: function(e, el)
35960 e.preventDefault();
35962 if(this.bgimage.length){
35963 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35964 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35968 leave: function(e, el)
35970 e.preventDefault();
35972 if(this.bgimage.length){
35973 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35974 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35989 * @class Roo.bootstrap.NumberField
35990 * @extends Roo.bootstrap.Input
35991 * Bootstrap NumberField class
35997 * Create a new NumberField
35998 * @param {Object} config The config object
36001 Roo.bootstrap.NumberField = function(config){
36002 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36005 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36008 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36010 allowDecimals : true,
36012 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36014 decimalSeparator : ".",
36016 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36018 decimalPrecision : 2,
36020 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36022 allowNegative : true,
36025 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36029 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36031 minValue : Number.NEGATIVE_INFINITY,
36033 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36035 maxValue : Number.MAX_VALUE,
36037 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36039 minText : "The minimum value for this field is {0}",
36041 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36043 maxText : "The maximum value for this field is {0}",
36045 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36046 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36048 nanText : "{0} is not a valid number",
36050 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36052 thousandsDelimiter : false,
36054 * @cfg {String} valueAlign alignment of value
36056 valueAlign : "left",
36058 getAutoCreate : function()
36060 var hiddenInput = {
36064 cls: 'hidden-number-input'
36068 hiddenInput.name = this.name;
36073 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36075 this.name = hiddenInput.name;
36077 if(cfg.cn.length > 0) {
36078 cfg.cn.push(hiddenInput);
36085 initEvents : function()
36087 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36089 var allowed = "0123456789";
36091 if(this.allowDecimals){
36092 allowed += this.decimalSeparator;
36095 if(this.allowNegative){
36099 if(this.thousandsDelimiter) {
36103 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36105 var keyPress = function(e){
36107 var k = e.getKey();
36109 var c = e.getCharCode();
36112 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36113 allowed.indexOf(String.fromCharCode(c)) === -1
36119 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36123 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36128 this.el.on("keypress", keyPress, this);
36131 validateValue : function(value)
36134 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36138 var num = this.parseValue(value);
36141 this.markInvalid(String.format(this.nanText, value));
36145 if(num < this.minValue){
36146 this.markInvalid(String.format(this.minText, this.minValue));
36150 if(num > this.maxValue){
36151 this.markInvalid(String.format(this.maxText, this.maxValue));
36158 getValue : function()
36160 var v = this.hiddenEl().getValue();
36162 return this.fixPrecision(this.parseValue(v));
36165 parseValue : function(value)
36167 if(this.thousandsDelimiter) {
36169 r = new RegExp(",", "g");
36170 value = value.replace(r, "");
36173 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36174 return isNaN(value) ? '' : value;
36177 fixPrecision : function(value)
36179 if(this.thousandsDelimiter) {
36181 r = new RegExp(",", "g");
36182 value = value.replace(r, "");
36185 var nan = isNaN(value);
36187 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36188 return nan ? '' : value;
36190 return parseFloat(value).toFixed(this.decimalPrecision);
36193 setValue : function(v)
36195 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36201 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36203 this.inputEl().dom.value = (v == '') ? '' :
36204 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36206 if(!this.allowZero && v === '0') {
36207 this.hiddenEl().dom.value = '';
36208 this.inputEl().dom.value = '';
36215 decimalPrecisionFcn : function(v)
36217 return Math.floor(v);
36220 beforeBlur : function()
36222 var v = this.parseValue(this.getRawValue());
36224 if(v || v === 0 || v === ''){
36229 hiddenEl : function()
36231 return this.el.select('input.hidden-number-input',true).first();
36243 * @class Roo.bootstrap.DocumentSlider
36244 * @extends Roo.bootstrap.Component
36245 * Bootstrap DocumentSlider class
36248 * Create a new DocumentViewer
36249 * @param {Object} config The config object
36252 Roo.bootstrap.DocumentSlider = function(config){
36253 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36260 * Fire after initEvent
36261 * @param {Roo.bootstrap.DocumentSlider} this
36266 * Fire after update
36267 * @param {Roo.bootstrap.DocumentSlider} this
36273 * @param {Roo.bootstrap.DocumentSlider} this
36279 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36285 getAutoCreate : function()
36289 cls : 'roo-document-slider',
36293 cls : 'roo-document-slider-header',
36297 cls : 'roo-document-slider-header-title'
36303 cls : 'roo-document-slider-body',
36307 cls : 'roo-document-slider-prev',
36311 cls : 'fa fa-chevron-left'
36317 cls : 'roo-document-slider-thumb',
36321 cls : 'roo-document-slider-image'
36327 cls : 'roo-document-slider-next',
36331 cls : 'fa fa-chevron-right'
36343 initEvents : function()
36345 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36346 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36348 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36349 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36351 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36352 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36354 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36355 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36357 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36358 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36360 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36361 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36363 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36364 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36366 this.thumbEl.on('click', this.onClick, this);
36368 this.prevIndicator.on('click', this.prev, this);
36370 this.nextIndicator.on('click', this.next, this);
36374 initial : function()
36376 if(this.files.length){
36377 this.indicator = 1;
36381 this.fireEvent('initial', this);
36384 update : function()
36386 this.imageEl.attr('src', this.files[this.indicator - 1]);
36388 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36390 this.prevIndicator.show();
36392 if(this.indicator == 1){
36393 this.prevIndicator.hide();
36396 this.nextIndicator.show();
36398 if(this.indicator == this.files.length){
36399 this.nextIndicator.hide();
36402 this.thumbEl.scrollTo('top');
36404 this.fireEvent('update', this);
36407 onClick : function(e)
36409 e.preventDefault();
36411 this.fireEvent('click', this);
36416 e.preventDefault();
36418 this.indicator = Math.max(1, this.indicator - 1);
36425 e.preventDefault();
36427 this.indicator = Math.min(this.files.length, this.indicator + 1);
36441 * @class Roo.bootstrap.RadioSet
36442 * @extends Roo.bootstrap.Input
36443 * Bootstrap RadioSet class
36444 * @cfg {String} indicatorpos (left|right) default left
36445 * @cfg {Boolean} inline (true|false) inline the element (default true)
36446 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36448 * Create a new RadioSet
36449 * @param {Object} config The config object
36452 Roo.bootstrap.RadioSet = function(config){
36454 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36458 Roo.bootstrap.RadioSet.register(this);
36463 * Fires when the element is checked or unchecked.
36464 * @param {Roo.bootstrap.RadioSet} this This radio
36465 * @param {Roo.bootstrap.Radio} item The checked item
36470 * Fires when the element is click.
36471 * @param {Roo.bootstrap.RadioSet} this This radio set
36472 * @param {Roo.bootstrap.Radio} item The checked item
36473 * @param {Roo.EventObject} e The event object
36480 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36488 indicatorpos : 'left',
36490 getAutoCreate : function()
36494 cls : 'roo-radio-set-label',
36498 html : this.fieldLabel
36502 if (Roo.bootstrap.version == 3) {
36505 if(this.indicatorpos == 'left'){
36508 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36509 tooltip : 'This field is required'
36514 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36515 tooltip : 'This field is required'
36521 cls : 'roo-radio-set-items'
36524 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36526 if (align === 'left' && this.fieldLabel.length) {
36529 cls : "roo-radio-set-right",
36535 if(this.labelWidth > 12){
36536 label.style = "width: " + this.labelWidth + 'px';
36539 if(this.labelWidth < 13 && this.labelmd == 0){
36540 this.labelmd = this.labelWidth;
36543 if(this.labellg > 0){
36544 label.cls += ' col-lg-' + this.labellg;
36545 items.cls += ' col-lg-' + (12 - this.labellg);
36548 if(this.labelmd > 0){
36549 label.cls += ' col-md-' + this.labelmd;
36550 items.cls += ' col-md-' + (12 - this.labelmd);
36553 if(this.labelsm > 0){
36554 label.cls += ' col-sm-' + this.labelsm;
36555 items.cls += ' col-sm-' + (12 - this.labelsm);
36558 if(this.labelxs > 0){
36559 label.cls += ' col-xs-' + this.labelxs;
36560 items.cls += ' col-xs-' + (12 - this.labelxs);
36566 cls : 'roo-radio-set',
36570 cls : 'roo-radio-set-input',
36573 value : this.value ? this.value : ''
36580 if(this.weight.length){
36581 cfg.cls += ' roo-radio-' + this.weight;
36585 cfg.cls += ' roo-radio-set-inline';
36589 ['xs','sm','md','lg'].map(function(size){
36590 if (settings[size]) {
36591 cfg.cls += ' col-' + size + '-' + settings[size];
36599 initEvents : function()
36601 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36602 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36604 if(!this.fieldLabel.length){
36605 this.labelEl.hide();
36608 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36609 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36611 this.indicator = this.indicatorEl();
36613 if(this.indicator){
36614 this.indicator.addClass('invisible');
36617 this.originalValue = this.getValue();
36621 inputEl: function ()
36623 return this.el.select('.roo-radio-set-input', true).first();
36626 getChildContainer : function()
36628 return this.itemsEl;
36631 register : function(item)
36633 this.radioes.push(item);
36637 validate : function()
36639 if(this.getVisibilityEl().hasClass('hidden')){
36645 Roo.each(this.radioes, function(i){
36654 if(this.allowBlank) {
36658 if(this.disabled || valid){
36663 this.markInvalid();
36668 markValid : function()
36670 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36671 this.indicatorEl().removeClass('visible');
36672 this.indicatorEl().addClass('invisible');
36676 if (Roo.bootstrap.version == 3) {
36677 this.el.removeClass([this.invalidClass, this.validClass]);
36678 this.el.addClass(this.validClass);
36680 this.el.removeClass(['is-invalid','is-valid']);
36681 this.el.addClass(['is-valid']);
36683 this.fireEvent('valid', this);
36686 markInvalid : function(msg)
36688 if(this.allowBlank || this.disabled){
36692 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36693 this.indicatorEl().removeClass('invisible');
36694 this.indicatorEl().addClass('visible');
36696 if (Roo.bootstrap.version == 3) {
36697 this.el.removeClass([this.invalidClass, this.validClass]);
36698 this.el.addClass(this.invalidClass);
36700 this.el.removeClass(['is-invalid','is-valid']);
36701 this.el.addClass(['is-invalid']);
36704 this.fireEvent('invalid', this, msg);
36708 setValue : function(v, suppressEvent)
36710 if(this.value === v){
36717 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36720 Roo.each(this.radioes, function(i){
36722 i.el.removeClass('checked');
36725 Roo.each(this.radioes, function(i){
36727 if(i.value === v || i.value.toString() === v.toString()){
36729 i.el.addClass('checked');
36731 if(suppressEvent !== true){
36732 this.fireEvent('check', this, i);
36743 clearInvalid : function(){
36745 if(!this.el || this.preventMark){
36749 this.el.removeClass([this.invalidClass]);
36751 this.fireEvent('valid', this);
36756 Roo.apply(Roo.bootstrap.RadioSet, {
36760 register : function(set)
36762 this.groups[set.name] = set;
36765 get: function(name)
36767 if (typeof(this.groups[name]) == 'undefined') {
36771 return this.groups[name] ;
36777 * Ext JS Library 1.1.1
36778 * Copyright(c) 2006-2007, Ext JS, LLC.
36780 * Originally Released Under LGPL - original licence link has changed is not relivant.
36783 * <script type="text/javascript">
36788 * @class Roo.bootstrap.SplitBar
36789 * @extends Roo.util.Observable
36790 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36794 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36795 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36796 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36797 split.minSize = 100;
36798 split.maxSize = 600;
36799 split.animate = true;
36800 split.on('moved', splitterMoved);
36803 * Create a new SplitBar
36804 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36805 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36806 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36807 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36808 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36809 position of the SplitBar).
36811 Roo.bootstrap.SplitBar = function(cfg){
36816 // dragElement : elm
36817 // resizingElement: el,
36819 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36820 // placement : Roo.bootstrap.SplitBar.LEFT ,
36821 // existingProxy ???
36824 this.el = Roo.get(cfg.dragElement, true);
36825 this.el.dom.unselectable = "on";
36827 this.resizingEl = Roo.get(cfg.resizingElement, true);
36831 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36832 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36835 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36838 * The minimum size of the resizing element. (Defaults to 0)
36844 * The maximum size of the resizing element. (Defaults to 2000)
36847 this.maxSize = 2000;
36850 * Whether to animate the transition to the new size
36853 this.animate = false;
36856 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36859 this.useShim = false;
36864 if(!cfg.existingProxy){
36866 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36868 this.proxy = Roo.get(cfg.existingProxy).dom;
36871 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36874 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36877 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36880 this.dragSpecs = {};
36883 * @private The adapter to use to positon and resize elements
36885 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36886 this.adapter.init(this);
36888 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36890 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36891 this.el.addClass("roo-splitbar-h");
36894 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36895 this.el.addClass("roo-splitbar-v");
36901 * Fires when the splitter is moved (alias for {@link #event-moved})
36902 * @param {Roo.bootstrap.SplitBar} this
36903 * @param {Number} newSize the new width or height
36908 * Fires when the splitter is moved
36909 * @param {Roo.bootstrap.SplitBar} this
36910 * @param {Number} newSize the new width or height
36914 * @event beforeresize
36915 * Fires before the splitter is dragged
36916 * @param {Roo.bootstrap.SplitBar} this
36918 "beforeresize" : true,
36920 "beforeapply" : true
36923 Roo.util.Observable.call(this);
36926 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36927 onStartProxyDrag : function(x, y){
36928 this.fireEvent("beforeresize", this);
36930 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36932 o.enableDisplayMode("block");
36933 // all splitbars share the same overlay
36934 Roo.bootstrap.SplitBar.prototype.overlay = o;
36936 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36937 this.overlay.show();
36938 Roo.get(this.proxy).setDisplayed("block");
36939 var size = this.adapter.getElementSize(this);
36940 this.activeMinSize = this.getMinimumSize();;
36941 this.activeMaxSize = this.getMaximumSize();;
36942 var c1 = size - this.activeMinSize;
36943 var c2 = Math.max(this.activeMaxSize - size, 0);
36944 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36945 this.dd.resetConstraints();
36946 this.dd.setXConstraint(
36947 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36948 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36950 this.dd.setYConstraint(0, 0);
36952 this.dd.resetConstraints();
36953 this.dd.setXConstraint(0, 0);
36954 this.dd.setYConstraint(
36955 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36956 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36959 this.dragSpecs.startSize = size;
36960 this.dragSpecs.startPoint = [x, y];
36961 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36965 * @private Called after the drag operation by the DDProxy
36967 onEndProxyDrag : function(e){
36968 Roo.get(this.proxy).setDisplayed(false);
36969 var endPoint = Roo.lib.Event.getXY(e);
36971 this.overlay.hide();
36974 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36975 newSize = this.dragSpecs.startSize +
36976 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36977 endPoint[0] - this.dragSpecs.startPoint[0] :
36978 this.dragSpecs.startPoint[0] - endPoint[0]
36981 newSize = this.dragSpecs.startSize +
36982 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36983 endPoint[1] - this.dragSpecs.startPoint[1] :
36984 this.dragSpecs.startPoint[1] - endPoint[1]
36987 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36988 if(newSize != this.dragSpecs.startSize){
36989 if(this.fireEvent('beforeapply', this, newSize) !== false){
36990 this.adapter.setElementSize(this, newSize);
36991 this.fireEvent("moved", this, newSize);
36992 this.fireEvent("resize", this, newSize);
36998 * Get the adapter this SplitBar uses
36999 * @return The adapter object
37001 getAdapter : function(){
37002 return this.adapter;
37006 * Set the adapter this SplitBar uses
37007 * @param {Object} adapter A SplitBar adapter object
37009 setAdapter : function(adapter){
37010 this.adapter = adapter;
37011 this.adapter.init(this);
37015 * Gets the minimum size for the resizing element
37016 * @return {Number} The minimum size
37018 getMinimumSize : function(){
37019 return this.minSize;
37023 * Sets the minimum size for the resizing element
37024 * @param {Number} minSize The minimum size
37026 setMinimumSize : function(minSize){
37027 this.minSize = minSize;
37031 * Gets the maximum size for the resizing element
37032 * @return {Number} The maximum size
37034 getMaximumSize : function(){
37035 return this.maxSize;
37039 * Sets the maximum size for the resizing element
37040 * @param {Number} maxSize The maximum size
37042 setMaximumSize : function(maxSize){
37043 this.maxSize = maxSize;
37047 * Sets the initialize size for the resizing element
37048 * @param {Number} size The initial size
37050 setCurrentSize : function(size){
37051 var oldAnimate = this.animate;
37052 this.animate = false;
37053 this.adapter.setElementSize(this, size);
37054 this.animate = oldAnimate;
37058 * Destroy this splitbar.
37059 * @param {Boolean} removeEl True to remove the element
37061 destroy : function(removeEl){
37063 this.shim.remove();
37066 this.proxy.parentNode.removeChild(this.proxy);
37074 * @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.
37076 Roo.bootstrap.SplitBar.createProxy = function(dir){
37077 var proxy = new Roo.Element(document.createElement("div"));
37078 proxy.unselectable();
37079 var cls = 'roo-splitbar-proxy';
37080 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37081 document.body.appendChild(proxy.dom);
37086 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37087 * Default Adapter. It assumes the splitter and resizing element are not positioned
37088 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37090 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37093 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37094 // do nothing for now
37095 init : function(s){
37099 * Called before drag operations to get the current size of the resizing element.
37100 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37102 getElementSize : function(s){
37103 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37104 return s.resizingEl.getWidth();
37106 return s.resizingEl.getHeight();
37111 * Called after drag operations to set the size of the resizing element.
37112 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37113 * @param {Number} newSize The new size to set
37114 * @param {Function} onComplete A function to be invoked when resizing is complete
37116 setElementSize : function(s, newSize, onComplete){
37117 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37119 s.resizingEl.setWidth(newSize);
37121 onComplete(s, newSize);
37124 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37129 s.resizingEl.setHeight(newSize);
37131 onComplete(s, newSize);
37134 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37141 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37142 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37143 * Adapter that moves the splitter element to align with the resized sizing element.
37144 * Used with an absolute positioned SplitBar.
37145 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37146 * document.body, make sure you assign an id to the body element.
37148 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37149 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37150 this.container = Roo.get(container);
37153 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37154 init : function(s){
37155 this.basic.init(s);
37158 getElementSize : function(s){
37159 return this.basic.getElementSize(s);
37162 setElementSize : function(s, newSize, onComplete){
37163 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37166 moveSplitter : function(s){
37167 var yes = Roo.bootstrap.SplitBar;
37168 switch(s.placement){
37170 s.el.setX(s.resizingEl.getRight());
37173 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37176 s.el.setY(s.resizingEl.getBottom());
37179 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37186 * Orientation constant - Create a vertical SplitBar
37190 Roo.bootstrap.SplitBar.VERTICAL = 1;
37193 * Orientation constant - Create a horizontal SplitBar
37197 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37200 * Placement constant - The resizing element is to the left of the splitter element
37204 Roo.bootstrap.SplitBar.LEFT = 1;
37207 * Placement constant - The resizing element is to the right of the splitter element
37211 Roo.bootstrap.SplitBar.RIGHT = 2;
37214 * Placement constant - The resizing element is positioned above the splitter element
37218 Roo.bootstrap.SplitBar.TOP = 3;
37221 * Placement constant - The resizing element is positioned under splitter element
37225 Roo.bootstrap.SplitBar.BOTTOM = 4;
37226 Roo.namespace("Roo.bootstrap.layout");/*
37228 * Ext JS Library 1.1.1
37229 * Copyright(c) 2006-2007, Ext JS, LLC.
37231 * Originally Released Under LGPL - original licence link has changed is not relivant.
37234 * <script type="text/javascript">
37238 * @class Roo.bootstrap.layout.Manager
37239 * @extends Roo.bootstrap.Component
37240 * Base class for layout managers.
37242 Roo.bootstrap.layout.Manager = function(config)
37244 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37250 /** false to disable window resize monitoring @type Boolean */
37251 this.monitorWindowResize = true;
37256 * Fires when a layout is performed.
37257 * @param {Roo.LayoutManager} this
37261 * @event regionresized
37262 * Fires when the user resizes a region.
37263 * @param {Roo.LayoutRegion} region The resized region
37264 * @param {Number} newSize The new size (width for east/west, height for north/south)
37266 "regionresized" : true,
37268 * @event regioncollapsed
37269 * Fires when a region is collapsed.
37270 * @param {Roo.LayoutRegion} region The collapsed region
37272 "regioncollapsed" : true,
37274 * @event regionexpanded
37275 * Fires when a region is expanded.
37276 * @param {Roo.LayoutRegion} region The expanded region
37278 "regionexpanded" : true
37280 this.updating = false;
37283 this.el = Roo.get(config.el);
37289 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37294 monitorWindowResize : true,
37300 onRender : function(ct, position)
37303 this.el = Roo.get(ct);
37306 //this.fireEvent('render',this);
37310 initEvents: function()
37314 // ie scrollbar fix
37315 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37316 document.body.scroll = "no";
37317 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37318 this.el.position('relative');
37320 this.id = this.el.id;
37321 this.el.addClass("roo-layout-container");
37322 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37323 if(this.el.dom != document.body ) {
37324 this.el.on('resize', this.layout,this);
37325 this.el.on('show', this.layout,this);
37331 * Returns true if this layout is currently being updated
37332 * @return {Boolean}
37334 isUpdating : function(){
37335 return this.updating;
37339 * Suspend the LayoutManager from doing auto-layouts while
37340 * making multiple add or remove calls
37342 beginUpdate : function(){
37343 this.updating = true;
37347 * Restore auto-layouts and optionally disable the manager from performing a layout
37348 * @param {Boolean} noLayout true to disable a layout update
37350 endUpdate : function(noLayout){
37351 this.updating = false;
37357 layout: function(){
37361 onRegionResized : function(region, newSize){
37362 this.fireEvent("regionresized", region, newSize);
37366 onRegionCollapsed : function(region){
37367 this.fireEvent("regioncollapsed", region);
37370 onRegionExpanded : function(region){
37371 this.fireEvent("regionexpanded", region);
37375 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37376 * performs box-model adjustments.
37377 * @return {Object} The size as an object {width: (the width), height: (the height)}
37379 getViewSize : function()
37382 if(this.el.dom != document.body){
37383 size = this.el.getSize();
37385 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37387 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37388 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37393 * Returns the Element this layout is bound to.
37394 * @return {Roo.Element}
37396 getEl : function(){
37401 * Returns the specified region.
37402 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37403 * @return {Roo.LayoutRegion}
37405 getRegion : function(target){
37406 return this.regions[target.toLowerCase()];
37409 onWindowResize : function(){
37410 if(this.monitorWindowResize){
37417 * Ext JS Library 1.1.1
37418 * Copyright(c) 2006-2007, Ext JS, LLC.
37420 * Originally Released Under LGPL - original licence link has changed is not relivant.
37423 * <script type="text/javascript">
37426 * @class Roo.bootstrap.layout.Border
37427 * @extends Roo.bootstrap.layout.Manager
37428 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37429 * please see: examples/bootstrap/nested.html<br><br>
37431 <b>The container the layout is rendered into can be either the body element or any other element.
37432 If it is not the body element, the container needs to either be an absolute positioned element,
37433 or you will need to add "position:relative" to the css of the container. You will also need to specify
37434 the container size if it is not the body element.</b>
37437 * Create a new Border
37438 * @param {Object} config Configuration options
37440 Roo.bootstrap.layout.Border = function(config){
37441 config = config || {};
37442 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37446 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37447 if(config[region]){
37448 config[region].region = region;
37449 this.addRegion(config[region]);
37455 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37457 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37459 parent : false, // this might point to a 'nest' or a ???
37462 * Creates and adds a new region if it doesn't already exist.
37463 * @param {String} target The target region key (north, south, east, west or center).
37464 * @param {Object} config The regions config object
37465 * @return {BorderLayoutRegion} The new region
37467 addRegion : function(config)
37469 if(!this.regions[config.region]){
37470 var r = this.factory(config);
37471 this.bindRegion(r);
37473 return this.regions[config.region];
37477 bindRegion : function(r){
37478 this.regions[r.config.region] = r;
37480 r.on("visibilitychange", this.layout, this);
37481 r.on("paneladded", this.layout, this);
37482 r.on("panelremoved", this.layout, this);
37483 r.on("invalidated", this.layout, this);
37484 r.on("resized", this.onRegionResized, this);
37485 r.on("collapsed", this.onRegionCollapsed, this);
37486 r.on("expanded", this.onRegionExpanded, this);
37490 * Performs a layout update.
37492 layout : function()
37494 if(this.updating) {
37498 // render all the rebions if they have not been done alreayd?
37499 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37500 if(this.regions[region] && !this.regions[region].bodyEl){
37501 this.regions[region].onRender(this.el)
37505 var size = this.getViewSize();
37506 var w = size.width;
37507 var h = size.height;
37512 //var x = 0, y = 0;
37514 var rs = this.regions;
37515 var north = rs["north"];
37516 var south = rs["south"];
37517 var west = rs["west"];
37518 var east = rs["east"];
37519 var center = rs["center"];
37520 //if(this.hideOnLayout){ // not supported anymore
37521 //c.el.setStyle("display", "none");
37523 if(north && north.isVisible()){
37524 var b = north.getBox();
37525 var m = north.getMargins();
37526 b.width = w - (m.left+m.right);
37529 centerY = b.height + b.y + m.bottom;
37530 centerH -= centerY;
37531 north.updateBox(this.safeBox(b));
37533 if(south && south.isVisible()){
37534 var b = south.getBox();
37535 var m = south.getMargins();
37536 b.width = w - (m.left+m.right);
37538 var totalHeight = (b.height + m.top + m.bottom);
37539 b.y = h - totalHeight + m.top;
37540 centerH -= totalHeight;
37541 south.updateBox(this.safeBox(b));
37543 if(west && west.isVisible()){
37544 var b = west.getBox();
37545 var m = west.getMargins();
37546 b.height = centerH - (m.top+m.bottom);
37548 b.y = centerY + m.top;
37549 var totalWidth = (b.width + m.left + m.right);
37550 centerX += totalWidth;
37551 centerW -= totalWidth;
37552 west.updateBox(this.safeBox(b));
37554 if(east && east.isVisible()){
37555 var b = east.getBox();
37556 var m = east.getMargins();
37557 b.height = centerH - (m.top+m.bottom);
37558 var totalWidth = (b.width + m.left + m.right);
37559 b.x = w - totalWidth + m.left;
37560 b.y = centerY + m.top;
37561 centerW -= totalWidth;
37562 east.updateBox(this.safeBox(b));
37565 var m = center.getMargins();
37567 x: centerX + m.left,
37568 y: centerY + m.top,
37569 width: centerW - (m.left+m.right),
37570 height: centerH - (m.top+m.bottom)
37572 //if(this.hideOnLayout){
37573 //center.el.setStyle("display", "block");
37575 center.updateBox(this.safeBox(centerBox));
37578 this.fireEvent("layout", this);
37582 safeBox : function(box){
37583 box.width = Math.max(0, box.width);
37584 box.height = Math.max(0, box.height);
37589 * Adds a ContentPanel (or subclass) to this layout.
37590 * @param {String} target The target region key (north, south, east, west or center).
37591 * @param {Roo.ContentPanel} panel The panel to add
37592 * @return {Roo.ContentPanel} The added panel
37594 add : function(target, panel){
37596 target = target.toLowerCase();
37597 return this.regions[target].add(panel);
37601 * Remove a ContentPanel (or subclass) to this layout.
37602 * @param {String} target The target region key (north, south, east, west or center).
37603 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37604 * @return {Roo.ContentPanel} The removed panel
37606 remove : function(target, panel){
37607 target = target.toLowerCase();
37608 return this.regions[target].remove(panel);
37612 * Searches all regions for a panel with the specified id
37613 * @param {String} panelId
37614 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37616 findPanel : function(panelId){
37617 var rs = this.regions;
37618 for(var target in rs){
37619 if(typeof rs[target] != "function"){
37620 var p = rs[target].getPanel(panelId);
37630 * Searches all regions for a panel with the specified id and activates (shows) it.
37631 * @param {String/ContentPanel} panelId The panels id or the panel itself
37632 * @return {Roo.ContentPanel} The shown panel or null
37634 showPanel : function(panelId) {
37635 var rs = this.regions;
37636 for(var target in rs){
37637 var r = rs[target];
37638 if(typeof r != "function"){
37639 if(r.hasPanel(panelId)){
37640 return r.showPanel(panelId);
37648 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37649 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37652 restoreState : function(provider){
37654 provider = Roo.state.Manager;
37656 var sm = new Roo.LayoutStateManager();
37657 sm.init(this, provider);
37663 * Adds a xtype elements to the layout.
37667 xtype : 'ContentPanel',
37674 xtype : 'NestedLayoutPanel',
37680 items : [ ... list of content panels or nested layout panels.. ]
37684 * @param {Object} cfg Xtype definition of item to add.
37686 addxtype : function(cfg)
37688 // basically accepts a pannel...
37689 // can accept a layout region..!?!?
37690 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37693 // theory? children can only be panels??
37695 //if (!cfg.xtype.match(/Panel$/)) {
37700 if (typeof(cfg.region) == 'undefined') {
37701 Roo.log("Failed to add Panel, region was not set");
37705 var region = cfg.region;
37711 xitems = cfg.items;
37716 if ( region == 'center') {
37717 Roo.log("Center: " + cfg.title);
37723 case 'Content': // ContentPanel (el, cfg)
37724 case 'Scroll': // ContentPanel (el, cfg)
37726 cfg.autoCreate = cfg.autoCreate || true;
37727 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37729 // var el = this.el.createChild();
37730 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37733 this.add(region, ret);
37737 case 'TreePanel': // our new panel!
37738 cfg.el = this.el.createChild();
37739 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37740 this.add(region, ret);
37745 // create a new Layout (which is a Border Layout...
37747 var clayout = cfg.layout;
37748 clayout.el = this.el.createChild();
37749 clayout.items = clayout.items || [];
37753 // replace this exitems with the clayout ones..
37754 xitems = clayout.items;
37756 // force background off if it's in center...
37757 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37758 cfg.background = false;
37760 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37763 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37764 //console.log('adding nested layout panel ' + cfg.toSource());
37765 this.add(region, ret);
37766 nb = {}; /// find first...
37771 // needs grid and region
37773 //var el = this.getRegion(region).el.createChild();
37775 *var el = this.el.createChild();
37776 // create the grid first...
37777 cfg.grid.container = el;
37778 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37781 if (region == 'center' && this.active ) {
37782 cfg.background = false;
37785 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37787 this.add(region, ret);
37789 if (cfg.background) {
37790 // render grid on panel activation (if panel background)
37791 ret.on('activate', function(gp) {
37792 if (!gp.grid.rendered) {
37793 // gp.grid.render(el);
37797 // cfg.grid.render(el);
37803 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37804 // it was the old xcomponent building that caused this before.
37805 // espeically if border is the top element in the tree.
37815 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37817 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37818 this.add(region, ret);
37822 throw "Can not add '" + cfg.xtype + "' to Border";
37828 this.beginUpdate();
37832 Roo.each(xitems, function(i) {
37833 region = nb && i.region ? i.region : false;
37835 var add = ret.addxtype(i);
37838 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37839 if (!i.background) {
37840 abn[region] = nb[region] ;
37847 // make the last non-background panel active..
37848 //if (nb) { Roo.log(abn); }
37851 for(var r in abn) {
37852 region = this.getRegion(r);
37854 // tried using nb[r], but it does not work..
37856 region.showPanel(abn[r]);
37867 factory : function(cfg)
37870 var validRegions = Roo.bootstrap.layout.Border.regions;
37872 var target = cfg.region;
37875 var r = Roo.bootstrap.layout;
37879 return new r.North(cfg);
37881 return new r.South(cfg);
37883 return new r.East(cfg);
37885 return new r.West(cfg);
37887 return new r.Center(cfg);
37889 throw 'Layout region "'+target+'" not supported.';
37896 * Ext JS Library 1.1.1
37897 * Copyright(c) 2006-2007, Ext JS, LLC.
37899 * Originally Released Under LGPL - original licence link has changed is not relivant.
37902 * <script type="text/javascript">
37906 * @class Roo.bootstrap.layout.Basic
37907 * @extends Roo.util.Observable
37908 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37909 * and does not have a titlebar, tabs or any other features. All it does is size and position
37910 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37911 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37912 * @cfg {string} region the region that it inhabits..
37913 * @cfg {bool} skipConfig skip config?
37917 Roo.bootstrap.layout.Basic = function(config){
37919 this.mgr = config.mgr;
37921 this.position = config.region;
37923 var skipConfig = config.skipConfig;
37927 * @scope Roo.BasicLayoutRegion
37931 * @event beforeremove
37932 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37933 * @param {Roo.LayoutRegion} this
37934 * @param {Roo.ContentPanel} panel The panel
37935 * @param {Object} e The cancel event object
37937 "beforeremove" : true,
37939 * @event invalidated
37940 * Fires when the layout for this region is changed.
37941 * @param {Roo.LayoutRegion} this
37943 "invalidated" : true,
37945 * @event visibilitychange
37946 * Fires when this region is shown or hidden
37947 * @param {Roo.LayoutRegion} this
37948 * @param {Boolean} visibility true or false
37950 "visibilitychange" : true,
37952 * @event paneladded
37953 * Fires when a panel is added.
37954 * @param {Roo.LayoutRegion} this
37955 * @param {Roo.ContentPanel} panel The panel
37957 "paneladded" : true,
37959 * @event panelremoved
37960 * Fires when a panel is removed.
37961 * @param {Roo.LayoutRegion} this
37962 * @param {Roo.ContentPanel} panel The panel
37964 "panelremoved" : true,
37966 * @event beforecollapse
37967 * Fires when this region before collapse.
37968 * @param {Roo.LayoutRegion} this
37970 "beforecollapse" : true,
37973 * Fires when this region is collapsed.
37974 * @param {Roo.LayoutRegion} this
37976 "collapsed" : true,
37979 * Fires when this region is expanded.
37980 * @param {Roo.LayoutRegion} this
37985 * Fires when this region is slid into view.
37986 * @param {Roo.LayoutRegion} this
37988 "slideshow" : true,
37991 * Fires when this region slides out of view.
37992 * @param {Roo.LayoutRegion} this
37994 "slidehide" : true,
37996 * @event panelactivated
37997 * Fires when a panel is activated.
37998 * @param {Roo.LayoutRegion} this
37999 * @param {Roo.ContentPanel} panel The activated panel
38001 "panelactivated" : true,
38004 * Fires when the user resizes this region.
38005 * @param {Roo.LayoutRegion} this
38006 * @param {Number} newSize The new size (width for east/west, height for north/south)
38010 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38011 this.panels = new Roo.util.MixedCollection();
38012 this.panels.getKey = this.getPanelId.createDelegate(this);
38014 this.activePanel = null;
38015 // ensure listeners are added...
38017 if (config.listeners || config.events) {
38018 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38019 listeners : config.listeners || {},
38020 events : config.events || {}
38024 if(skipConfig !== true){
38025 this.applyConfig(config);
38029 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38031 getPanelId : function(p){
38035 applyConfig : function(config){
38036 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38037 this.config = config;
38042 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38043 * the width, for horizontal (north, south) the height.
38044 * @param {Number} newSize The new width or height
38046 resizeTo : function(newSize){
38047 var el = this.el ? this.el :
38048 (this.activePanel ? this.activePanel.getEl() : null);
38050 switch(this.position){
38053 el.setWidth(newSize);
38054 this.fireEvent("resized", this, newSize);
38058 el.setHeight(newSize);
38059 this.fireEvent("resized", this, newSize);
38065 getBox : function(){
38066 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38069 getMargins : function(){
38070 return this.margins;
38073 updateBox : function(box){
38075 var el = this.activePanel.getEl();
38076 el.dom.style.left = box.x + "px";
38077 el.dom.style.top = box.y + "px";
38078 this.activePanel.setSize(box.width, box.height);
38082 * Returns the container element for this region.
38083 * @return {Roo.Element}
38085 getEl : function(){
38086 return this.activePanel;
38090 * Returns true if this region is currently visible.
38091 * @return {Boolean}
38093 isVisible : function(){
38094 return this.activePanel ? true : false;
38097 setActivePanel : function(panel){
38098 panel = this.getPanel(panel);
38099 if(this.activePanel && this.activePanel != panel){
38100 this.activePanel.setActiveState(false);
38101 this.activePanel.getEl().setLeftTop(-10000,-10000);
38103 this.activePanel = panel;
38104 panel.setActiveState(true);
38106 panel.setSize(this.box.width, this.box.height);
38108 this.fireEvent("panelactivated", this, panel);
38109 this.fireEvent("invalidated");
38113 * Show the specified panel.
38114 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38115 * @return {Roo.ContentPanel} The shown panel or null
38117 showPanel : function(panel){
38118 panel = this.getPanel(panel);
38120 this.setActivePanel(panel);
38126 * Get the active panel for this region.
38127 * @return {Roo.ContentPanel} The active panel or null
38129 getActivePanel : function(){
38130 return this.activePanel;
38134 * Add the passed ContentPanel(s)
38135 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38136 * @return {Roo.ContentPanel} The panel added (if only one was added)
38138 add : function(panel){
38139 if(arguments.length > 1){
38140 for(var i = 0, len = arguments.length; i < len; i++) {
38141 this.add(arguments[i]);
38145 if(this.hasPanel(panel)){
38146 this.showPanel(panel);
38149 var el = panel.getEl();
38150 if(el.dom.parentNode != this.mgr.el.dom){
38151 this.mgr.el.dom.appendChild(el.dom);
38153 if(panel.setRegion){
38154 panel.setRegion(this);
38156 this.panels.add(panel);
38157 el.setStyle("position", "absolute");
38158 if(!panel.background){
38159 this.setActivePanel(panel);
38160 if(this.config.initialSize && this.panels.getCount()==1){
38161 this.resizeTo(this.config.initialSize);
38164 this.fireEvent("paneladded", this, panel);
38169 * Returns true if the panel is in this region.
38170 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38171 * @return {Boolean}
38173 hasPanel : function(panel){
38174 if(typeof panel == "object"){ // must be panel obj
38175 panel = panel.getId();
38177 return this.getPanel(panel) ? true : false;
38181 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38182 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38183 * @param {Boolean} preservePanel Overrides the config preservePanel option
38184 * @return {Roo.ContentPanel} The panel that was removed
38186 remove : function(panel, preservePanel){
38187 panel = this.getPanel(panel);
38192 this.fireEvent("beforeremove", this, panel, e);
38193 if(e.cancel === true){
38196 var panelId = panel.getId();
38197 this.panels.removeKey(panelId);
38202 * Returns the panel specified or null if it's not in this region.
38203 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38204 * @return {Roo.ContentPanel}
38206 getPanel : function(id){
38207 if(typeof id == "object"){ // must be panel obj
38210 return this.panels.get(id);
38214 * Returns this regions position (north/south/east/west/center).
38217 getPosition: function(){
38218 return this.position;
38222 * Ext JS Library 1.1.1
38223 * Copyright(c) 2006-2007, Ext JS, LLC.
38225 * Originally Released Under LGPL - original licence link has changed is not relivant.
38228 * <script type="text/javascript">
38232 * @class Roo.bootstrap.layout.Region
38233 * @extends Roo.bootstrap.layout.Basic
38234 * This class represents a region in a layout manager.
38236 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38237 * @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})
38238 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38239 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38240 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38241 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38242 * @cfg {String} title The title for the region (overrides panel titles)
38243 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38244 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38245 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38246 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38247 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38248 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38249 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38250 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38251 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38252 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38254 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38255 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38256 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38257 * @cfg {Number} width For East/West panels
38258 * @cfg {Number} height For North/South panels
38259 * @cfg {Boolean} split To show the splitter
38260 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38262 * @cfg {string} cls Extra CSS classes to add to region
38264 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38265 * @cfg {string} region the region that it inhabits..
38268 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38269 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38271 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38272 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38273 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38275 Roo.bootstrap.layout.Region = function(config)
38277 this.applyConfig(config);
38279 var mgr = config.mgr;
38280 var pos = config.region;
38281 config.skipConfig = true;
38282 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38285 this.onRender(mgr.el);
38288 this.visible = true;
38289 this.collapsed = false;
38290 this.unrendered_panels = [];
38293 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38295 position: '', // set by wrapper (eg. north/south etc..)
38296 unrendered_panels : null, // unrendered panels.
38298 tabPosition : false,
38300 mgr: false, // points to 'Border'
38303 createBody : function(){
38304 /** This region's body element
38305 * @type Roo.Element */
38306 this.bodyEl = this.el.createChild({
38308 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38312 onRender: function(ctr, pos)
38314 var dh = Roo.DomHelper;
38315 /** This region's container element
38316 * @type Roo.Element */
38317 this.el = dh.append(ctr.dom, {
38319 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38321 /** This region's title element
38322 * @type Roo.Element */
38324 this.titleEl = dh.append(this.el.dom, {
38326 unselectable: "on",
38327 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38329 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38330 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38334 this.titleEl.enableDisplayMode();
38335 /** This region's title text element
38336 * @type HTMLElement */
38337 this.titleTextEl = this.titleEl.dom.firstChild;
38338 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38340 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38341 this.closeBtn.enableDisplayMode();
38342 this.closeBtn.on("click", this.closeClicked, this);
38343 this.closeBtn.hide();
38345 this.createBody(this.config);
38346 if(this.config.hideWhenEmpty){
38348 this.on("paneladded", this.validateVisibility, this);
38349 this.on("panelremoved", this.validateVisibility, this);
38351 if(this.autoScroll){
38352 this.bodyEl.setStyle("overflow", "auto");
38354 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38356 //if(c.titlebar !== false){
38357 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38358 this.titleEl.hide();
38360 this.titleEl.show();
38361 if(this.config.title){
38362 this.titleTextEl.innerHTML = this.config.title;
38366 if(this.config.collapsed){
38367 this.collapse(true);
38369 if(this.config.hidden){
38373 if (this.unrendered_panels && this.unrendered_panels.length) {
38374 for (var i =0;i< this.unrendered_panels.length; i++) {
38375 this.add(this.unrendered_panels[i]);
38377 this.unrendered_panels = null;
38383 applyConfig : function(c)
38386 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38387 var dh = Roo.DomHelper;
38388 if(c.titlebar !== false){
38389 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38390 this.collapseBtn.on("click", this.collapse, this);
38391 this.collapseBtn.enableDisplayMode();
38393 if(c.showPin === true || this.showPin){
38394 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38395 this.stickBtn.enableDisplayMode();
38396 this.stickBtn.on("click", this.expand, this);
38397 this.stickBtn.hide();
38402 /** This region's collapsed element
38403 * @type Roo.Element */
38406 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38407 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38410 if(c.floatable !== false){
38411 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38412 this.collapsedEl.on("click", this.collapseClick, this);
38415 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38416 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38417 id: "message", unselectable: "on", style:{"float":"left"}});
38418 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38420 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38421 this.expandBtn.on("click", this.expand, this);
38425 if(this.collapseBtn){
38426 this.collapseBtn.setVisible(c.collapsible == true);
38429 this.cmargins = c.cmargins || this.cmargins ||
38430 (this.position == "west" || this.position == "east" ?
38431 {top: 0, left: 2, right:2, bottom: 0} :
38432 {top: 2, left: 0, right:0, bottom: 2});
38434 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38437 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38439 this.autoScroll = c.autoScroll || false;
38444 this.duration = c.duration || .30;
38445 this.slideDuration = c.slideDuration || .45;
38450 * Returns true if this region is currently visible.
38451 * @return {Boolean}
38453 isVisible : function(){
38454 return this.visible;
38458 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38459 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38461 //setCollapsedTitle : function(title){
38462 // title = title || " ";
38463 // if(this.collapsedTitleTextEl){
38464 // this.collapsedTitleTextEl.innerHTML = title;
38468 getBox : function(){
38470 // if(!this.collapsed){
38471 b = this.el.getBox(false, true);
38473 // b = this.collapsedEl.getBox(false, true);
38478 getMargins : function(){
38479 return this.margins;
38480 //return this.collapsed ? this.cmargins : this.margins;
38483 highlight : function(){
38484 this.el.addClass("x-layout-panel-dragover");
38487 unhighlight : function(){
38488 this.el.removeClass("x-layout-panel-dragover");
38491 updateBox : function(box)
38493 if (!this.bodyEl) {
38494 return; // not rendered yet..
38498 if(!this.collapsed){
38499 this.el.dom.style.left = box.x + "px";
38500 this.el.dom.style.top = box.y + "px";
38501 this.updateBody(box.width, box.height);
38503 this.collapsedEl.dom.style.left = box.x + "px";
38504 this.collapsedEl.dom.style.top = box.y + "px";
38505 this.collapsedEl.setSize(box.width, box.height);
38508 this.tabs.autoSizeTabs();
38512 updateBody : function(w, h)
38515 this.el.setWidth(w);
38516 w -= this.el.getBorderWidth("rl");
38517 if(this.config.adjustments){
38518 w += this.config.adjustments[0];
38521 if(h !== null && h > 0){
38522 this.el.setHeight(h);
38523 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38524 h -= this.el.getBorderWidth("tb");
38525 if(this.config.adjustments){
38526 h += this.config.adjustments[1];
38528 this.bodyEl.setHeight(h);
38530 h = this.tabs.syncHeight(h);
38533 if(this.panelSize){
38534 w = w !== null ? w : this.panelSize.width;
38535 h = h !== null ? h : this.panelSize.height;
38537 if(this.activePanel){
38538 var el = this.activePanel.getEl();
38539 w = w !== null ? w : el.getWidth();
38540 h = h !== null ? h : el.getHeight();
38541 this.panelSize = {width: w, height: h};
38542 this.activePanel.setSize(w, h);
38544 if(Roo.isIE && this.tabs){
38545 this.tabs.el.repaint();
38550 * Returns the container element for this region.
38551 * @return {Roo.Element}
38553 getEl : function(){
38558 * Hides this region.
38561 //if(!this.collapsed){
38562 this.el.dom.style.left = "-2000px";
38565 // this.collapsedEl.dom.style.left = "-2000px";
38566 // this.collapsedEl.hide();
38568 this.visible = false;
38569 this.fireEvent("visibilitychange", this, false);
38573 * Shows this region if it was previously hidden.
38576 //if(!this.collapsed){
38579 // this.collapsedEl.show();
38581 this.visible = true;
38582 this.fireEvent("visibilitychange", this, true);
38585 closeClicked : function(){
38586 if(this.activePanel){
38587 this.remove(this.activePanel);
38591 collapseClick : function(e){
38593 e.stopPropagation();
38596 e.stopPropagation();
38602 * Collapses this region.
38603 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38606 collapse : function(skipAnim, skipCheck = false){
38607 if(this.collapsed) {
38611 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38613 this.collapsed = true;
38615 this.split.el.hide();
38617 if(this.config.animate && skipAnim !== true){
38618 this.fireEvent("invalidated", this);
38619 this.animateCollapse();
38621 this.el.setLocation(-20000,-20000);
38623 this.collapsedEl.show();
38624 this.fireEvent("collapsed", this);
38625 this.fireEvent("invalidated", this);
38631 animateCollapse : function(){
38636 * Expands this region if it was previously collapsed.
38637 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38638 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38641 expand : function(e, skipAnim){
38643 e.stopPropagation();
38645 if(!this.collapsed || this.el.hasActiveFx()) {
38649 this.afterSlideIn();
38652 this.collapsed = false;
38653 if(this.config.animate && skipAnim !== true){
38654 this.animateExpand();
38658 this.split.el.show();
38660 this.collapsedEl.setLocation(-2000,-2000);
38661 this.collapsedEl.hide();
38662 this.fireEvent("invalidated", this);
38663 this.fireEvent("expanded", this);
38667 animateExpand : function(){
38671 initTabs : function()
38673 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38675 var ts = new Roo.bootstrap.panel.Tabs({
38676 el: this.bodyEl.dom,
38678 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38679 disableTooltips: this.config.disableTabTips,
38680 toolbar : this.config.toolbar
38683 if(this.config.hideTabs){
38684 ts.stripWrap.setDisplayed(false);
38687 ts.resizeTabs = this.config.resizeTabs === true;
38688 ts.minTabWidth = this.config.minTabWidth || 40;
38689 ts.maxTabWidth = this.config.maxTabWidth || 250;
38690 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38691 ts.monitorResize = false;
38692 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38693 ts.bodyEl.addClass('roo-layout-tabs-body');
38694 this.panels.each(this.initPanelAsTab, this);
38697 initPanelAsTab : function(panel){
38698 var ti = this.tabs.addTab(
38702 this.config.closeOnTab && panel.isClosable(),
38705 if(panel.tabTip !== undefined){
38706 ti.setTooltip(panel.tabTip);
38708 ti.on("activate", function(){
38709 this.setActivePanel(panel);
38712 if(this.config.closeOnTab){
38713 ti.on("beforeclose", function(t, e){
38715 this.remove(panel);
38719 panel.tabItem = ti;
38724 updatePanelTitle : function(panel, title)
38726 if(this.activePanel == panel){
38727 this.updateTitle(title);
38730 var ti = this.tabs.getTab(panel.getEl().id);
38732 if(panel.tabTip !== undefined){
38733 ti.setTooltip(panel.tabTip);
38738 updateTitle : function(title){
38739 if(this.titleTextEl && !this.config.title){
38740 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38744 setActivePanel : function(panel)
38746 panel = this.getPanel(panel);
38747 if(this.activePanel && this.activePanel != panel){
38748 if(this.activePanel.setActiveState(false) === false){
38752 this.activePanel = panel;
38753 panel.setActiveState(true);
38754 if(this.panelSize){
38755 panel.setSize(this.panelSize.width, this.panelSize.height);
38758 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38760 this.updateTitle(panel.getTitle());
38762 this.fireEvent("invalidated", this);
38764 this.fireEvent("panelactivated", this, panel);
38768 * Shows the specified panel.
38769 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38770 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38772 showPanel : function(panel)
38774 panel = this.getPanel(panel);
38777 var tab = this.tabs.getTab(panel.getEl().id);
38778 if(tab.isHidden()){
38779 this.tabs.unhideTab(tab.id);
38783 this.setActivePanel(panel);
38790 * Get the active panel for this region.
38791 * @return {Roo.ContentPanel} The active panel or null
38793 getActivePanel : function(){
38794 return this.activePanel;
38797 validateVisibility : function(){
38798 if(this.panels.getCount() < 1){
38799 this.updateTitle(" ");
38800 this.closeBtn.hide();
38803 if(!this.isVisible()){
38810 * Adds the passed ContentPanel(s) to this region.
38811 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38812 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38814 add : function(panel)
38816 if(arguments.length > 1){
38817 for(var i = 0, len = arguments.length; i < len; i++) {
38818 this.add(arguments[i]);
38823 // if we have not been rendered yet, then we can not really do much of this..
38824 if (!this.bodyEl) {
38825 this.unrendered_panels.push(panel);
38832 if(this.hasPanel(panel)){
38833 this.showPanel(panel);
38836 panel.setRegion(this);
38837 this.panels.add(panel);
38838 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38839 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38840 // and hide them... ???
38841 this.bodyEl.dom.appendChild(panel.getEl().dom);
38842 if(panel.background !== true){
38843 this.setActivePanel(panel);
38845 this.fireEvent("paneladded", this, panel);
38852 this.initPanelAsTab(panel);
38856 if(panel.background !== true){
38857 this.tabs.activate(panel.getEl().id);
38859 this.fireEvent("paneladded", this, panel);
38864 * Hides the tab for the specified panel.
38865 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38867 hidePanel : function(panel){
38868 if(this.tabs && (panel = this.getPanel(panel))){
38869 this.tabs.hideTab(panel.getEl().id);
38874 * Unhides the tab for a previously hidden panel.
38875 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38877 unhidePanel : function(panel){
38878 if(this.tabs && (panel = this.getPanel(panel))){
38879 this.tabs.unhideTab(panel.getEl().id);
38883 clearPanels : function(){
38884 while(this.panels.getCount() > 0){
38885 this.remove(this.panels.first());
38890 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38891 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38892 * @param {Boolean} preservePanel Overrides the config preservePanel option
38893 * @return {Roo.ContentPanel} The panel that was removed
38895 remove : function(panel, preservePanel)
38897 panel = this.getPanel(panel);
38902 this.fireEvent("beforeremove", this, panel, e);
38903 if(e.cancel === true){
38906 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38907 var panelId = panel.getId();
38908 this.panels.removeKey(panelId);
38910 document.body.appendChild(panel.getEl().dom);
38913 this.tabs.removeTab(panel.getEl().id);
38914 }else if (!preservePanel){
38915 this.bodyEl.dom.removeChild(panel.getEl().dom);
38917 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38918 var p = this.panels.first();
38919 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38920 tempEl.appendChild(p.getEl().dom);
38921 this.bodyEl.update("");
38922 this.bodyEl.dom.appendChild(p.getEl().dom);
38924 this.updateTitle(p.getTitle());
38926 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38927 this.setActivePanel(p);
38929 panel.setRegion(null);
38930 if(this.activePanel == panel){
38931 this.activePanel = null;
38933 if(this.config.autoDestroy !== false && preservePanel !== true){
38934 try{panel.destroy();}catch(e){}
38936 this.fireEvent("panelremoved", this, panel);
38941 * Returns the TabPanel component used by this region
38942 * @return {Roo.TabPanel}
38944 getTabs : function(){
38948 createTool : function(parentEl, className){
38949 var btn = Roo.DomHelper.append(parentEl, {
38951 cls: "x-layout-tools-button",
38954 cls: "roo-layout-tools-button-inner " + className,
38958 btn.addClassOnOver("roo-layout-tools-button-over");
38963 * Ext JS Library 1.1.1
38964 * Copyright(c) 2006-2007, Ext JS, LLC.
38966 * Originally Released Under LGPL - original licence link has changed is not relivant.
38969 * <script type="text/javascript">
38975 * @class Roo.SplitLayoutRegion
38976 * @extends Roo.LayoutRegion
38977 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38979 Roo.bootstrap.layout.Split = function(config){
38980 this.cursor = config.cursor;
38981 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38984 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38986 splitTip : "Drag to resize.",
38987 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38988 useSplitTips : false,
38990 applyConfig : function(config){
38991 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38994 onRender : function(ctr,pos) {
38996 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38997 if(!this.config.split){
39002 var splitEl = Roo.DomHelper.append(ctr.dom, {
39004 id: this.el.id + "-split",
39005 cls: "roo-layout-split roo-layout-split-"+this.position,
39008 /** The SplitBar for this region
39009 * @type Roo.SplitBar */
39010 // does not exist yet...
39011 Roo.log([this.position, this.orientation]);
39013 this.split = new Roo.bootstrap.SplitBar({
39014 dragElement : splitEl,
39015 resizingElement: this.el,
39016 orientation : this.orientation
39019 this.split.on("moved", this.onSplitMove, this);
39020 this.split.useShim = this.config.useShim === true;
39021 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39022 if(this.useSplitTips){
39023 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39025 //if(config.collapsible){
39026 // this.split.el.on("dblclick", this.collapse, this);
39029 if(typeof this.config.minSize != "undefined"){
39030 this.split.minSize = this.config.minSize;
39032 if(typeof this.config.maxSize != "undefined"){
39033 this.split.maxSize = this.config.maxSize;
39035 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39036 this.hideSplitter();
39041 getHMaxSize : function(){
39042 var cmax = this.config.maxSize || 10000;
39043 var center = this.mgr.getRegion("center");
39044 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39047 getVMaxSize : function(){
39048 var cmax = this.config.maxSize || 10000;
39049 var center = this.mgr.getRegion("center");
39050 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39053 onSplitMove : function(split, newSize){
39054 this.fireEvent("resized", this, newSize);
39058 * Returns the {@link Roo.SplitBar} for this region.
39059 * @return {Roo.SplitBar}
39061 getSplitBar : function(){
39066 this.hideSplitter();
39067 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39070 hideSplitter : function(){
39072 this.split.el.setLocation(-2000,-2000);
39073 this.split.el.hide();
39079 this.split.el.show();
39081 Roo.bootstrap.layout.Split.superclass.show.call(this);
39084 beforeSlide: function(){
39085 if(Roo.isGecko){// firefox overflow auto bug workaround
39086 this.bodyEl.clip();
39088 this.tabs.bodyEl.clip();
39090 if(this.activePanel){
39091 this.activePanel.getEl().clip();
39093 if(this.activePanel.beforeSlide){
39094 this.activePanel.beforeSlide();
39100 afterSlide : function(){
39101 if(Roo.isGecko){// firefox overflow auto bug workaround
39102 this.bodyEl.unclip();
39104 this.tabs.bodyEl.unclip();
39106 if(this.activePanel){
39107 this.activePanel.getEl().unclip();
39108 if(this.activePanel.afterSlide){
39109 this.activePanel.afterSlide();
39115 initAutoHide : function(){
39116 if(this.autoHide !== false){
39117 if(!this.autoHideHd){
39118 var st = new Roo.util.DelayedTask(this.slideIn, this);
39119 this.autoHideHd = {
39120 "mouseout": function(e){
39121 if(!e.within(this.el, true)){
39125 "mouseover" : function(e){
39131 this.el.on(this.autoHideHd);
39135 clearAutoHide : function(){
39136 if(this.autoHide !== false){
39137 this.el.un("mouseout", this.autoHideHd.mouseout);
39138 this.el.un("mouseover", this.autoHideHd.mouseover);
39142 clearMonitor : function(){
39143 Roo.get(document).un("click", this.slideInIf, this);
39146 // these names are backwards but not changed for compat
39147 slideOut : function(){
39148 if(this.isSlid || this.el.hasActiveFx()){
39151 this.isSlid = true;
39152 if(this.collapseBtn){
39153 this.collapseBtn.hide();
39155 this.closeBtnState = this.closeBtn.getStyle('display');
39156 this.closeBtn.hide();
39158 this.stickBtn.show();
39161 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39162 this.beforeSlide();
39163 this.el.setStyle("z-index", 10001);
39164 this.el.slideIn(this.getSlideAnchor(), {
39165 callback: function(){
39167 this.initAutoHide();
39168 Roo.get(document).on("click", this.slideInIf, this);
39169 this.fireEvent("slideshow", this);
39176 afterSlideIn : function(){
39177 this.clearAutoHide();
39178 this.isSlid = false;
39179 this.clearMonitor();
39180 this.el.setStyle("z-index", "");
39181 if(this.collapseBtn){
39182 this.collapseBtn.show();
39184 this.closeBtn.setStyle('display', this.closeBtnState);
39186 this.stickBtn.hide();
39188 this.fireEvent("slidehide", this);
39191 slideIn : function(cb){
39192 if(!this.isSlid || this.el.hasActiveFx()){
39196 this.isSlid = false;
39197 this.beforeSlide();
39198 this.el.slideOut(this.getSlideAnchor(), {
39199 callback: function(){
39200 this.el.setLeftTop(-10000, -10000);
39202 this.afterSlideIn();
39210 slideInIf : function(e){
39211 if(!e.within(this.el)){
39216 animateCollapse : function(){
39217 this.beforeSlide();
39218 this.el.setStyle("z-index", 20000);
39219 var anchor = this.getSlideAnchor();
39220 this.el.slideOut(anchor, {
39221 callback : function(){
39222 this.el.setStyle("z-index", "");
39223 this.collapsedEl.slideIn(anchor, {duration:.3});
39225 this.el.setLocation(-10000,-10000);
39227 this.fireEvent("collapsed", this);
39234 animateExpand : function(){
39235 this.beforeSlide();
39236 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39237 this.el.setStyle("z-index", 20000);
39238 this.collapsedEl.hide({
39241 this.el.slideIn(this.getSlideAnchor(), {
39242 callback : function(){
39243 this.el.setStyle("z-index", "");
39246 this.split.el.show();
39248 this.fireEvent("invalidated", this);
39249 this.fireEvent("expanded", this);
39277 getAnchor : function(){
39278 return this.anchors[this.position];
39281 getCollapseAnchor : function(){
39282 return this.canchors[this.position];
39285 getSlideAnchor : function(){
39286 return this.sanchors[this.position];
39289 getAlignAdj : function(){
39290 var cm = this.cmargins;
39291 switch(this.position){
39307 getExpandAdj : function(){
39308 var c = this.collapsedEl, cm = this.cmargins;
39309 switch(this.position){
39311 return [-(cm.right+c.getWidth()+cm.left), 0];
39314 return [cm.right+c.getWidth()+cm.left, 0];
39317 return [0, -(cm.top+cm.bottom+c.getHeight())];
39320 return [0, cm.top+cm.bottom+c.getHeight()];
39326 * Ext JS Library 1.1.1
39327 * Copyright(c) 2006-2007, Ext JS, LLC.
39329 * Originally Released Under LGPL - original licence link has changed is not relivant.
39332 * <script type="text/javascript">
39335 * These classes are private internal classes
39337 Roo.bootstrap.layout.Center = function(config){
39338 config.region = "center";
39339 Roo.bootstrap.layout.Region.call(this, config);
39340 this.visible = true;
39341 this.minWidth = config.minWidth || 20;
39342 this.minHeight = config.minHeight || 20;
39345 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39347 // center panel can't be hidden
39351 // center panel can't be hidden
39354 getMinWidth: function(){
39355 return this.minWidth;
39358 getMinHeight: function(){
39359 return this.minHeight;
39373 Roo.bootstrap.layout.North = function(config)
39375 config.region = 'north';
39376 config.cursor = 'n-resize';
39378 Roo.bootstrap.layout.Split.call(this, config);
39382 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39383 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39384 this.split.el.addClass("roo-layout-split-v");
39386 //var size = config.initialSize || config.height;
39387 //if(this.el && typeof size != "undefined"){
39388 // this.el.setHeight(size);
39391 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39393 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39396 onRender : function(ctr, pos)
39398 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39399 var size = this.config.initialSize || this.config.height;
39400 if(this.el && typeof size != "undefined"){
39401 this.el.setHeight(size);
39406 getBox : function(){
39407 if(this.collapsed){
39408 return this.collapsedEl.getBox();
39410 var box = this.el.getBox();
39412 box.height += this.split.el.getHeight();
39417 updateBox : function(box){
39418 if(this.split && !this.collapsed){
39419 box.height -= this.split.el.getHeight();
39420 this.split.el.setLeft(box.x);
39421 this.split.el.setTop(box.y+box.height);
39422 this.split.el.setWidth(box.width);
39424 if(this.collapsed){
39425 this.updateBody(box.width, null);
39427 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39435 Roo.bootstrap.layout.South = function(config){
39436 config.region = 'south';
39437 config.cursor = 's-resize';
39438 Roo.bootstrap.layout.Split.call(this, config);
39440 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39441 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39442 this.split.el.addClass("roo-layout-split-v");
39447 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39448 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39450 onRender : function(ctr, pos)
39452 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39453 var size = this.config.initialSize || this.config.height;
39454 if(this.el && typeof size != "undefined"){
39455 this.el.setHeight(size);
39460 getBox : function(){
39461 if(this.collapsed){
39462 return this.collapsedEl.getBox();
39464 var box = this.el.getBox();
39466 var sh = this.split.el.getHeight();
39473 updateBox : function(box){
39474 if(this.split && !this.collapsed){
39475 var sh = this.split.el.getHeight();
39478 this.split.el.setLeft(box.x);
39479 this.split.el.setTop(box.y-sh);
39480 this.split.el.setWidth(box.width);
39482 if(this.collapsed){
39483 this.updateBody(box.width, null);
39485 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39489 Roo.bootstrap.layout.East = function(config){
39490 config.region = "east";
39491 config.cursor = "e-resize";
39492 Roo.bootstrap.layout.Split.call(this, config);
39494 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39495 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39496 this.split.el.addClass("roo-layout-split-h");
39500 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39501 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39503 onRender : function(ctr, pos)
39505 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39506 var size = this.config.initialSize || this.config.width;
39507 if(this.el && typeof size != "undefined"){
39508 this.el.setWidth(size);
39513 getBox : function(){
39514 if(this.collapsed){
39515 return this.collapsedEl.getBox();
39517 var box = this.el.getBox();
39519 var sw = this.split.el.getWidth();
39526 updateBox : function(box){
39527 if(this.split && !this.collapsed){
39528 var sw = this.split.el.getWidth();
39530 this.split.el.setLeft(box.x);
39531 this.split.el.setTop(box.y);
39532 this.split.el.setHeight(box.height);
39535 if(this.collapsed){
39536 this.updateBody(null, box.height);
39538 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39542 Roo.bootstrap.layout.West = function(config){
39543 config.region = "west";
39544 config.cursor = "w-resize";
39546 Roo.bootstrap.layout.Split.call(this, config);
39548 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39549 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39550 this.split.el.addClass("roo-layout-split-h");
39554 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39555 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39557 onRender: function(ctr, pos)
39559 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39560 var size = this.config.initialSize || this.config.width;
39561 if(typeof size != "undefined"){
39562 this.el.setWidth(size);
39566 getBox : function(){
39567 if(this.collapsed){
39568 return this.collapsedEl.getBox();
39570 var box = this.el.getBox();
39571 if (box.width == 0) {
39572 box.width = this.config.width; // kludge?
39575 box.width += this.split.el.getWidth();
39580 updateBox : function(box){
39581 if(this.split && !this.collapsed){
39582 var sw = this.split.el.getWidth();
39584 this.split.el.setLeft(box.x+box.width);
39585 this.split.el.setTop(box.y);
39586 this.split.el.setHeight(box.height);
39588 if(this.collapsed){
39589 this.updateBody(null, box.height);
39591 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39593 });Roo.namespace("Roo.bootstrap.panel");/*
39595 * Ext JS Library 1.1.1
39596 * Copyright(c) 2006-2007, Ext JS, LLC.
39598 * Originally Released Under LGPL - original licence link has changed is not relivant.
39601 * <script type="text/javascript">
39604 * @class Roo.ContentPanel
39605 * @extends Roo.util.Observable
39606 * A basic ContentPanel element.
39607 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39608 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39609 * @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
39610 * @cfg {Boolean} closable True if the panel can be closed/removed
39611 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39612 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39613 * @cfg {Toolbar} toolbar A toolbar for this panel
39614 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39615 * @cfg {String} title The title for this panel
39616 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39617 * @cfg {String} url Calls {@link #setUrl} with this value
39618 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39619 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39620 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39621 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39622 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39623 * @cfg {Boolean} badges render the badges
39624 * @cfg {String} cls extra classes to use
39625 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39628 * Create a new ContentPanel.
39629 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39630 * @param {String/Object} config A string to set only the title or a config object
39631 * @param {String} content (optional) Set the HTML content for this panel
39632 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39634 Roo.bootstrap.panel.Content = function( config){
39636 this.tpl = config.tpl || false;
39638 var el = config.el;
39639 var content = config.content;
39641 if(config.autoCreate){ // xtype is available if this is called from factory
39644 this.el = Roo.get(el);
39645 if(!this.el && config && config.autoCreate){
39646 if(typeof config.autoCreate == "object"){
39647 if(!config.autoCreate.id){
39648 config.autoCreate.id = config.id||el;
39650 this.el = Roo.DomHelper.append(document.body,
39651 config.autoCreate, true);
39655 cls: (config.cls || '') +
39656 (config.background ? ' bg-' + config.background : '') +
39657 " roo-layout-inactive-content",
39660 if (config.iframe) {
39664 style : 'border: 0px',
39665 src : 'about:blank'
39671 elcfg.html = config.html;
39675 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39676 if (config.iframe) {
39677 this.iframeEl = this.el.select('iframe',true).first();
39682 this.closable = false;
39683 this.loaded = false;
39684 this.active = false;
39687 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39689 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39691 this.wrapEl = this.el; //this.el.wrap();
39693 if (config.toolbar.items) {
39694 ti = config.toolbar.items ;
39695 delete config.toolbar.items ;
39699 this.toolbar.render(this.wrapEl, 'before');
39700 for(var i =0;i < ti.length;i++) {
39701 // Roo.log(['add child', items[i]]);
39702 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39704 this.toolbar.items = nitems;
39705 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39706 delete config.toolbar;
39710 // xtype created footer. - not sure if will work as we normally have to render first..
39711 if (this.footer && !this.footer.el && this.footer.xtype) {
39712 if (!this.wrapEl) {
39713 this.wrapEl = this.el.wrap();
39716 this.footer.container = this.wrapEl.createChild();
39718 this.footer = Roo.factory(this.footer, Roo);
39723 if(typeof config == "string"){
39724 this.title = config;
39726 Roo.apply(this, config);
39730 this.resizeEl = Roo.get(this.resizeEl, true);
39732 this.resizeEl = this.el;
39734 // handle view.xtype
39742 * Fires when this panel is activated.
39743 * @param {Roo.ContentPanel} this
39747 * @event deactivate
39748 * Fires when this panel is activated.
39749 * @param {Roo.ContentPanel} this
39751 "deactivate" : true,
39755 * Fires when this panel is resized if fitToFrame is true.
39756 * @param {Roo.ContentPanel} this
39757 * @param {Number} width The width after any component adjustments
39758 * @param {Number} height The height after any component adjustments
39764 * Fires when this tab is created
39765 * @param {Roo.ContentPanel} this
39776 if(this.autoScroll && !this.iframe){
39777 this.resizeEl.setStyle("overflow", "auto");
39779 // fix randome scrolling
39780 //this.el.on('scroll', function() {
39781 // Roo.log('fix random scolling');
39782 // this.scrollTo('top',0);
39785 content = content || this.content;
39787 this.setContent(content);
39789 if(config && config.url){
39790 this.setUrl(this.url, this.params, this.loadOnce);
39795 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39797 if (this.view && typeof(this.view.xtype) != 'undefined') {
39798 this.view.el = this.el.appendChild(document.createElement("div"));
39799 this.view = Roo.factory(this.view);
39800 this.view.render && this.view.render(false, '');
39804 this.fireEvent('render', this);
39807 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39817 setRegion : function(region){
39818 this.region = region;
39819 this.setActiveClass(region && !this.background);
39823 setActiveClass: function(state)
39826 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39827 this.el.setStyle('position','relative');
39829 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39830 this.el.setStyle('position', 'absolute');
39835 * Returns the toolbar for this Panel if one was configured.
39836 * @return {Roo.Toolbar}
39838 getToolbar : function(){
39839 return this.toolbar;
39842 setActiveState : function(active)
39844 this.active = active;
39845 this.setActiveClass(active);
39847 if(this.fireEvent("deactivate", this) === false){
39852 this.fireEvent("activate", this);
39856 * Updates this panel's element (not for iframe)
39857 * @param {String} content The new content
39858 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39860 setContent : function(content, loadScripts){
39865 this.el.update(content, loadScripts);
39868 ignoreResize : function(w, h){
39869 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39872 this.lastSize = {width: w, height: h};
39877 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39878 * @return {Roo.UpdateManager} The UpdateManager
39880 getUpdateManager : function(){
39884 return this.el.getUpdateManager();
39887 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39888 * Does not work with IFRAME contents
39889 * @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:
39892 url: "your-url.php",
39893 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39894 callback: yourFunction,
39895 scope: yourObject, //(optional scope)
39898 text: "Loading...",
39904 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39905 * 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.
39906 * @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}
39907 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39908 * @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.
39909 * @return {Roo.ContentPanel} this
39917 var um = this.el.getUpdateManager();
39918 um.update.apply(um, arguments);
39924 * 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.
39925 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39926 * @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)
39927 * @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)
39928 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39930 setUrl : function(url, params, loadOnce){
39932 this.iframeEl.dom.src = url;
39936 if(this.refreshDelegate){
39937 this.removeListener("activate", this.refreshDelegate);
39939 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39940 this.on("activate", this.refreshDelegate);
39941 return this.el.getUpdateManager();
39944 _handleRefresh : function(url, params, loadOnce){
39945 if(!loadOnce || !this.loaded){
39946 var updater = this.el.getUpdateManager();
39947 updater.update(url, params, this._setLoaded.createDelegate(this));
39951 _setLoaded : function(){
39952 this.loaded = true;
39956 * Returns this panel's id
39959 getId : function(){
39964 * Returns this panel's element - used by regiosn to add.
39965 * @return {Roo.Element}
39967 getEl : function(){
39968 return this.wrapEl || this.el;
39973 adjustForComponents : function(width, height)
39975 //Roo.log('adjustForComponents ');
39976 if(this.resizeEl != this.el){
39977 width -= this.el.getFrameWidth('lr');
39978 height -= this.el.getFrameWidth('tb');
39981 var te = this.toolbar.getEl();
39982 te.setWidth(width);
39983 height -= te.getHeight();
39986 var te = this.footer.getEl();
39987 te.setWidth(width);
39988 height -= te.getHeight();
39992 if(this.adjustments){
39993 width += this.adjustments[0];
39994 height += this.adjustments[1];
39996 return {"width": width, "height": height};
39999 setSize : function(width, height){
40000 if(this.fitToFrame && !this.ignoreResize(width, height)){
40001 if(this.fitContainer && this.resizeEl != this.el){
40002 this.el.setSize(width, height);
40004 var size = this.adjustForComponents(width, height);
40006 this.iframeEl.setSize(width,height);
40009 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40010 this.fireEvent('resize', this, size.width, size.height);
40017 * Returns this panel's title
40020 getTitle : function(){
40022 if (typeof(this.title) != 'object') {
40027 for (var k in this.title) {
40028 if (!this.title.hasOwnProperty(k)) {
40032 if (k.indexOf('-') >= 0) {
40033 var s = k.split('-');
40034 for (var i = 0; i<s.length; i++) {
40035 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40038 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40045 * Set this panel's title
40046 * @param {String} title
40048 setTitle : function(title){
40049 this.title = title;
40051 this.region.updatePanelTitle(this, title);
40056 * Returns true is this panel was configured to be closable
40057 * @return {Boolean}
40059 isClosable : function(){
40060 return this.closable;
40063 beforeSlide : function(){
40065 this.resizeEl.clip();
40068 afterSlide : function(){
40070 this.resizeEl.unclip();
40074 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40075 * Will fail silently if the {@link #setUrl} method has not been called.
40076 * This does not activate the panel, just updates its content.
40078 refresh : function(){
40079 if(this.refreshDelegate){
40080 this.loaded = false;
40081 this.refreshDelegate();
40086 * Destroys this panel
40088 destroy : function(){
40089 this.el.removeAllListeners();
40090 var tempEl = document.createElement("span");
40091 tempEl.appendChild(this.el.dom);
40092 tempEl.innerHTML = "";
40098 * form - if the content panel contains a form - this is a reference to it.
40099 * @type {Roo.form.Form}
40103 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40104 * This contains a reference to it.
40110 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40120 * @param {Object} cfg Xtype definition of item to add.
40124 getChildContainer: function () {
40125 return this.getEl();
40130 var ret = new Roo.factory(cfg);
40135 if (cfg.xtype.match(/^Form$/)) {
40138 //if (this.footer) {
40139 // el = this.footer.container.insertSibling(false, 'before');
40141 el = this.el.createChild();
40144 this.form = new Roo.form.Form(cfg);
40147 if ( this.form.allItems.length) {
40148 this.form.render(el.dom);
40152 // should only have one of theses..
40153 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40154 // views.. should not be just added - used named prop 'view''
40156 cfg.el = this.el.appendChild(document.createElement("div"));
40159 var ret = new Roo.factory(cfg);
40161 ret.render && ret.render(false, ''); // render blank..
40171 * @class Roo.bootstrap.panel.Grid
40172 * @extends Roo.bootstrap.panel.Content
40174 * Create a new GridPanel.
40175 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40176 * @param {Object} config A the config object
40182 Roo.bootstrap.panel.Grid = function(config)
40186 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40187 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40189 config.el = this.wrapper;
40190 //this.el = this.wrapper;
40192 if (config.container) {
40193 // ctor'ed from a Border/panel.grid
40196 this.wrapper.setStyle("overflow", "hidden");
40197 this.wrapper.addClass('roo-grid-container');
40202 if(config.toolbar){
40203 var tool_el = this.wrapper.createChild();
40204 this.toolbar = Roo.factory(config.toolbar);
40206 if (config.toolbar.items) {
40207 ti = config.toolbar.items ;
40208 delete config.toolbar.items ;
40212 this.toolbar.render(tool_el);
40213 for(var i =0;i < ti.length;i++) {
40214 // Roo.log(['add child', items[i]]);
40215 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40217 this.toolbar.items = nitems;
40219 delete config.toolbar;
40222 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40223 config.grid.scrollBody = true;;
40224 config.grid.monitorWindowResize = false; // turn off autosizing
40225 config.grid.autoHeight = false;
40226 config.grid.autoWidth = false;
40228 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40230 if (config.background) {
40231 // render grid on panel activation (if panel background)
40232 this.on('activate', function(gp) {
40233 if (!gp.grid.rendered) {
40234 gp.grid.render(this.wrapper);
40235 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40240 this.grid.render(this.wrapper);
40241 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40244 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40245 // ??? needed ??? config.el = this.wrapper;
40250 // xtype created footer. - not sure if will work as we normally have to render first..
40251 if (this.footer && !this.footer.el && this.footer.xtype) {
40253 var ctr = this.grid.getView().getFooterPanel(true);
40254 this.footer.dataSource = this.grid.dataSource;
40255 this.footer = Roo.factory(this.footer, Roo);
40256 this.footer.render(ctr);
40266 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40267 getId : function(){
40268 return this.grid.id;
40272 * Returns the grid for this panel
40273 * @return {Roo.bootstrap.Table}
40275 getGrid : function(){
40279 setSize : function(width, height){
40280 if(!this.ignoreResize(width, height)){
40281 var grid = this.grid;
40282 var size = this.adjustForComponents(width, height);
40283 // tfoot is not a footer?
40286 var gridel = grid.getGridEl();
40287 gridel.setSize(size.width, size.height);
40289 var tbd = grid.getGridEl().select('tbody', true).first();
40290 var thd = grid.getGridEl().select('thead',true).first();
40291 var tbf= grid.getGridEl().select('tfoot', true).first();
40294 size.height -= tbf.getHeight();
40297 size.height -= thd.getHeight();
40300 tbd.setSize(size.width, size.height );
40301 // this is for the account management tab -seems to work there.
40302 var thd = grid.getGridEl().select('thead',true).first();
40304 // tbd.setSize(size.width, size.height - thd.getHeight());
40313 beforeSlide : function(){
40314 this.grid.getView().scroller.clip();
40317 afterSlide : function(){
40318 this.grid.getView().scroller.unclip();
40321 destroy : function(){
40322 this.grid.destroy();
40324 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40329 * @class Roo.bootstrap.panel.Nest
40330 * @extends Roo.bootstrap.panel.Content
40332 * Create a new Panel, that can contain a layout.Border.
40335 * @param {Roo.BorderLayout} layout The layout for this panel
40336 * @param {String/Object} config A string to set only the title or a config object
40338 Roo.bootstrap.panel.Nest = function(config)
40340 // construct with only one argument..
40341 /* FIXME - implement nicer consturctors
40342 if (layout.layout) {
40344 layout = config.layout;
40345 delete config.layout;
40347 if (layout.xtype && !layout.getEl) {
40348 // then layout needs constructing..
40349 layout = Roo.factory(layout, Roo);
40353 config.el = config.layout.getEl();
40355 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40357 config.layout.monitorWindowResize = false; // turn off autosizing
40358 this.layout = config.layout;
40359 this.layout.getEl().addClass("roo-layout-nested-layout");
40360 this.layout.parent = this;
40367 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40369 setSize : function(width, height){
40370 if(!this.ignoreResize(width, height)){
40371 var size = this.adjustForComponents(width, height);
40372 var el = this.layout.getEl();
40373 if (size.height < 1) {
40374 el.setWidth(size.width);
40376 el.setSize(size.width, size.height);
40378 var touch = el.dom.offsetWidth;
40379 this.layout.layout();
40380 // ie requires a double layout on the first pass
40381 if(Roo.isIE && !this.initialized){
40382 this.initialized = true;
40383 this.layout.layout();
40388 // activate all subpanels if not currently active..
40390 setActiveState : function(active){
40391 this.active = active;
40392 this.setActiveClass(active);
40395 this.fireEvent("deactivate", this);
40399 this.fireEvent("activate", this);
40400 // not sure if this should happen before or after..
40401 if (!this.layout) {
40402 return; // should not happen..
40405 for (var r in this.layout.regions) {
40406 reg = this.layout.getRegion(r);
40407 if (reg.getActivePanel()) {
40408 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40409 reg.setActivePanel(reg.getActivePanel());
40412 if (!reg.panels.length) {
40415 reg.showPanel(reg.getPanel(0));
40424 * Returns the nested BorderLayout for this panel
40425 * @return {Roo.BorderLayout}
40427 getLayout : function(){
40428 return this.layout;
40432 * Adds a xtype elements to the layout of the nested panel
40436 xtype : 'ContentPanel',
40443 xtype : 'NestedLayoutPanel',
40449 items : [ ... list of content panels or nested layout panels.. ]
40453 * @param {Object} cfg Xtype definition of item to add.
40455 addxtype : function(cfg) {
40456 return this.layout.addxtype(cfg);
40461 * Ext JS Library 1.1.1
40462 * Copyright(c) 2006-2007, Ext JS, LLC.
40464 * Originally Released Under LGPL - original licence link has changed is not relivant.
40467 * <script type="text/javascript">
40470 * @class Roo.TabPanel
40471 * @extends Roo.util.Observable
40472 * A lightweight tab container.
40476 // basic tabs 1, built from existing content
40477 var tabs = new Roo.TabPanel("tabs1");
40478 tabs.addTab("script", "View Script");
40479 tabs.addTab("markup", "View Markup");
40480 tabs.activate("script");
40482 // more advanced tabs, built from javascript
40483 var jtabs = new Roo.TabPanel("jtabs");
40484 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40486 // set up the UpdateManager
40487 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40488 var updater = tab2.getUpdateManager();
40489 updater.setDefaultUrl("ajax1.htm");
40490 tab2.on('activate', updater.refresh, updater, true);
40492 // Use setUrl for Ajax loading
40493 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40494 tab3.setUrl("ajax2.htm", null, true);
40497 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40500 jtabs.activate("jtabs-1");
40503 * Create a new TabPanel.
40504 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40505 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40507 Roo.bootstrap.panel.Tabs = function(config){
40509 * The container element for this TabPanel.
40510 * @type Roo.Element
40512 this.el = Roo.get(config.el);
40515 if(typeof config == "boolean"){
40516 this.tabPosition = config ? "bottom" : "top";
40518 Roo.apply(this, config);
40522 if(this.tabPosition == "bottom"){
40523 // if tabs are at the bottom = create the body first.
40524 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40525 this.el.addClass("roo-tabs-bottom");
40527 // next create the tabs holders
40529 if (this.tabPosition == "west"){
40531 var reg = this.region; // fake it..
40533 if (!reg.mgr.parent) {
40536 reg = reg.mgr.parent.region;
40538 Roo.log("got nest?");
40540 if (reg.mgr.getRegion('west')) {
40541 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40542 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40543 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40544 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40545 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40553 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40554 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40555 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40556 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40561 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40564 // finally - if tabs are at the top, then create the body last..
40565 if(this.tabPosition != "bottom"){
40566 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40567 * @type Roo.Element
40569 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40570 this.el.addClass("roo-tabs-top");
40574 this.bodyEl.setStyle("position", "relative");
40576 this.active = null;
40577 this.activateDelegate = this.activate.createDelegate(this);
40582 * Fires when the active tab changes
40583 * @param {Roo.TabPanel} this
40584 * @param {Roo.TabPanelItem} activePanel The new active tab
40588 * @event beforetabchange
40589 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40590 * @param {Roo.TabPanel} this
40591 * @param {Object} e Set cancel to true on this object to cancel the tab change
40592 * @param {Roo.TabPanelItem} tab The tab being changed to
40594 "beforetabchange" : true
40597 Roo.EventManager.onWindowResize(this.onResize, this);
40598 this.cpad = this.el.getPadding("lr");
40599 this.hiddenCount = 0;
40602 // toolbar on the tabbar support...
40603 if (this.toolbar) {
40604 alert("no toolbar support yet");
40605 this.toolbar = false;
40607 var tcfg = this.toolbar;
40608 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40609 this.toolbar = new Roo.Toolbar(tcfg);
40610 if (Roo.isSafari) {
40611 var tbl = tcfg.container.child('table', true);
40612 tbl.setAttribute('width', '100%');
40620 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40623 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40625 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40627 tabPosition : "top",
40629 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40631 currentTabWidth : 0,
40633 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40637 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40641 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40643 preferredTabWidth : 175,
40645 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40647 resizeTabs : false,
40649 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40651 monitorResize : true,
40653 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40655 toolbar : false, // set by caller..
40657 region : false, /// set by caller
40659 disableTooltips : true, // not used yet...
40662 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40663 * @param {String} id The id of the div to use <b>or create</b>
40664 * @param {String} text The text for the tab
40665 * @param {String} content (optional) Content to put in the TabPanelItem body
40666 * @param {Boolean} closable (optional) True to create a close icon on the tab
40667 * @return {Roo.TabPanelItem} The created TabPanelItem
40669 addTab : function(id, text, content, closable, tpl)
40671 var item = new Roo.bootstrap.panel.TabItem({
40675 closable : closable,
40678 this.addTabItem(item);
40680 item.setContent(content);
40686 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40687 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40688 * @return {Roo.TabPanelItem}
40690 getTab : function(id){
40691 return this.items[id];
40695 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40696 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40698 hideTab : function(id){
40699 var t = this.items[id];
40702 this.hiddenCount++;
40703 this.autoSizeTabs();
40708 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40709 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40711 unhideTab : function(id){
40712 var t = this.items[id];
40714 t.setHidden(false);
40715 this.hiddenCount--;
40716 this.autoSizeTabs();
40721 * Adds an existing {@link Roo.TabPanelItem}.
40722 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40724 addTabItem : function(item)
40726 this.items[item.id] = item;
40727 this.items.push(item);
40728 this.autoSizeTabs();
40729 // if(this.resizeTabs){
40730 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40731 // this.autoSizeTabs();
40733 // item.autoSize();
40738 * Removes a {@link Roo.TabPanelItem}.
40739 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40741 removeTab : function(id){
40742 var items = this.items;
40743 var tab = items[id];
40744 if(!tab) { return; }
40745 var index = items.indexOf(tab);
40746 if(this.active == tab && items.length > 1){
40747 var newTab = this.getNextAvailable(index);
40752 this.stripEl.dom.removeChild(tab.pnode.dom);
40753 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40754 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40756 items.splice(index, 1);
40757 delete this.items[tab.id];
40758 tab.fireEvent("close", tab);
40759 tab.purgeListeners();
40760 this.autoSizeTabs();
40763 getNextAvailable : function(start){
40764 var items = this.items;
40766 // look for a next tab that will slide over to
40767 // replace the one being removed
40768 while(index < items.length){
40769 var item = items[++index];
40770 if(item && !item.isHidden()){
40774 // if one isn't found select the previous tab (on the left)
40777 var item = items[--index];
40778 if(item && !item.isHidden()){
40786 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40787 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40789 disableTab : function(id){
40790 var tab = this.items[id];
40791 if(tab && this.active != tab){
40797 * Enables a {@link Roo.TabPanelItem} that is disabled.
40798 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40800 enableTab : function(id){
40801 var tab = this.items[id];
40806 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40807 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40808 * @return {Roo.TabPanelItem} The TabPanelItem.
40810 activate : function(id)
40812 //Roo.log('activite:' + id);
40814 var tab = this.items[id];
40818 if(tab == this.active || tab.disabled){
40822 this.fireEvent("beforetabchange", this, e, tab);
40823 if(e.cancel !== true && !tab.disabled){
40825 this.active.hide();
40827 this.active = this.items[id];
40828 this.active.show();
40829 this.fireEvent("tabchange", this, this.active);
40835 * Gets the active {@link Roo.TabPanelItem}.
40836 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40838 getActiveTab : function(){
40839 return this.active;
40843 * Updates the tab body element to fit the height of the container element
40844 * for overflow scrolling
40845 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40847 syncHeight : function(targetHeight){
40848 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40849 var bm = this.bodyEl.getMargins();
40850 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40851 this.bodyEl.setHeight(newHeight);
40855 onResize : function(){
40856 if(this.monitorResize){
40857 this.autoSizeTabs();
40862 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40864 beginUpdate : function(){
40865 this.updating = true;
40869 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40871 endUpdate : function(){
40872 this.updating = false;
40873 this.autoSizeTabs();
40877 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40879 autoSizeTabs : function()
40881 var count = this.items.length;
40882 var vcount = count - this.hiddenCount;
40885 this.stripEl.hide();
40887 this.stripEl.show();
40890 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40895 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40896 var availWidth = Math.floor(w / vcount);
40897 var b = this.stripBody;
40898 if(b.getWidth() > w){
40899 var tabs = this.items;
40900 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40901 if(availWidth < this.minTabWidth){
40902 /*if(!this.sleft){ // incomplete scrolling code
40903 this.createScrollButtons();
40906 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40909 if(this.currentTabWidth < this.preferredTabWidth){
40910 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40916 * Returns the number of tabs in this TabPanel.
40919 getCount : function(){
40920 return this.items.length;
40924 * Resizes all the tabs to the passed width
40925 * @param {Number} The new width
40927 setTabWidth : function(width){
40928 this.currentTabWidth = width;
40929 for(var i = 0, len = this.items.length; i < len; i++) {
40930 if(!this.items[i].isHidden()) {
40931 this.items[i].setWidth(width);
40937 * Destroys this TabPanel
40938 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40940 destroy : function(removeEl){
40941 Roo.EventManager.removeResizeListener(this.onResize, this);
40942 for(var i = 0, len = this.items.length; i < len; i++){
40943 this.items[i].purgeListeners();
40945 if(removeEl === true){
40946 this.el.update("");
40951 createStrip : function(container)
40953 var strip = document.createElement("nav");
40954 strip.className = Roo.bootstrap.version == 4 ?
40955 "navbar-light bg-light" :
40956 "navbar navbar-default"; //"x-tabs-wrap";
40957 container.appendChild(strip);
40961 createStripList : function(strip)
40963 // div wrapper for retard IE
40964 // returns the "tr" element.
40965 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40966 //'<div class="x-tabs-strip-wrap">'+
40967 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40968 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40969 return strip.firstChild; //.firstChild.firstChild.firstChild;
40971 createBody : function(container)
40973 var body = document.createElement("div");
40974 Roo.id(body, "tab-body");
40975 //Roo.fly(body).addClass("x-tabs-body");
40976 Roo.fly(body).addClass("tab-content");
40977 container.appendChild(body);
40980 createItemBody :function(bodyEl, id){
40981 var body = Roo.getDom(id);
40983 body = document.createElement("div");
40986 //Roo.fly(body).addClass("x-tabs-item-body");
40987 Roo.fly(body).addClass("tab-pane");
40988 bodyEl.insertBefore(body, bodyEl.firstChild);
40992 createStripElements : function(stripEl, text, closable, tpl)
40994 var td = document.createElement("li"); // was td..
40995 td.className = 'nav-item';
40997 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41000 stripEl.appendChild(td);
41002 td.className = "x-tabs-closable";
41003 if(!this.closeTpl){
41004 this.closeTpl = new Roo.Template(
41005 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41006 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41007 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41010 var el = this.closeTpl.overwrite(td, {"text": text});
41011 var close = el.getElementsByTagName("div")[0];
41012 var inner = el.getElementsByTagName("em")[0];
41013 return {"el": el, "close": close, "inner": inner};
41016 // not sure what this is..
41017 // if(!this.tabTpl){
41018 //this.tabTpl = new Roo.Template(
41019 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41020 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41022 // this.tabTpl = new Roo.Template(
41023 // '<a href="#">' +
41024 // '<span unselectable="on"' +
41025 // (this.disableTooltips ? '' : ' title="{text}"') +
41026 // ' >{text}</span></a>'
41032 var template = tpl || this.tabTpl || false;
41035 template = new Roo.Template(
41036 Roo.bootstrap.version == 4 ?
41038 '<a class="nav-link" href="#" unselectable="on"' +
41039 (this.disableTooltips ? '' : ' title="{text}"') +
41042 '<a class="nav-link" href="#">' +
41043 '<span unselectable="on"' +
41044 (this.disableTooltips ? '' : ' title="{text}"') +
41045 ' >{text}</span></a>'
41050 switch (typeof(template)) {
41054 template = new Roo.Template(template);
41060 var el = template.overwrite(td, {"text": text});
41062 var inner = el.getElementsByTagName("span")[0];
41064 return {"el": el, "inner": inner};
41072 * @class Roo.TabPanelItem
41073 * @extends Roo.util.Observable
41074 * Represents an individual item (tab plus body) in a TabPanel.
41075 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41076 * @param {String} id The id of this TabPanelItem
41077 * @param {String} text The text for the tab of this TabPanelItem
41078 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41080 Roo.bootstrap.panel.TabItem = function(config){
41082 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41083 * @type Roo.TabPanel
41085 this.tabPanel = config.panel;
41087 * The id for this TabPanelItem
41090 this.id = config.id;
41092 this.disabled = false;
41094 this.text = config.text;
41096 this.loaded = false;
41097 this.closable = config.closable;
41100 * The body element for this TabPanelItem.
41101 * @type Roo.Element
41103 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41104 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41105 this.bodyEl.setStyle("display", "block");
41106 this.bodyEl.setStyle("zoom", "1");
41107 //this.hideAction();
41109 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41111 this.el = Roo.get(els.el);
41112 this.inner = Roo.get(els.inner, true);
41113 this.textEl = Roo.bootstrap.version == 4 ?
41114 this.el : Roo.get(this.el.dom.firstChild, true);
41116 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41117 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41120 // this.el.on("mousedown", this.onTabMouseDown, this);
41121 this.el.on("click", this.onTabClick, this);
41123 if(config.closable){
41124 var c = Roo.get(els.close, true);
41125 c.dom.title = this.closeText;
41126 c.addClassOnOver("close-over");
41127 c.on("click", this.closeClick, this);
41133 * Fires when this tab becomes the active tab.
41134 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41135 * @param {Roo.TabPanelItem} this
41139 * @event beforeclose
41140 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41141 * @param {Roo.TabPanelItem} this
41142 * @param {Object} e Set cancel to true on this object to cancel the close.
41144 "beforeclose": true,
41147 * Fires when this tab is closed.
41148 * @param {Roo.TabPanelItem} this
41152 * @event deactivate
41153 * Fires when this tab is no longer the active tab.
41154 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41155 * @param {Roo.TabPanelItem} this
41157 "deactivate" : true
41159 this.hidden = false;
41161 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41164 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41166 purgeListeners : function(){
41167 Roo.util.Observable.prototype.purgeListeners.call(this);
41168 this.el.removeAllListeners();
41171 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41174 this.status_node.addClass("active");
41177 this.tabPanel.stripWrap.repaint();
41179 this.fireEvent("activate", this.tabPanel, this);
41183 * Returns true if this tab is the active tab.
41184 * @return {Boolean}
41186 isActive : function(){
41187 return this.tabPanel.getActiveTab() == this;
41191 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41194 this.status_node.removeClass("active");
41196 this.fireEvent("deactivate", this.tabPanel, this);
41199 hideAction : function(){
41200 this.bodyEl.hide();
41201 this.bodyEl.setStyle("position", "absolute");
41202 this.bodyEl.setLeft("-20000px");
41203 this.bodyEl.setTop("-20000px");
41206 showAction : function(){
41207 this.bodyEl.setStyle("position", "relative");
41208 this.bodyEl.setTop("");
41209 this.bodyEl.setLeft("");
41210 this.bodyEl.show();
41214 * Set the tooltip for the tab.
41215 * @param {String} tooltip The tab's tooltip
41217 setTooltip : function(text){
41218 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41219 this.textEl.dom.qtip = text;
41220 this.textEl.dom.removeAttribute('title');
41222 this.textEl.dom.title = text;
41226 onTabClick : function(e){
41227 e.preventDefault();
41228 this.tabPanel.activate(this.id);
41231 onTabMouseDown : function(e){
41232 e.preventDefault();
41233 this.tabPanel.activate(this.id);
41236 getWidth : function(){
41237 return this.inner.getWidth();
41240 setWidth : function(width){
41241 var iwidth = width - this.linode.getPadding("lr");
41242 this.inner.setWidth(iwidth);
41243 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41244 this.linode.setWidth(width);
41248 * Show or hide the tab
41249 * @param {Boolean} hidden True to hide or false to show.
41251 setHidden : function(hidden){
41252 this.hidden = hidden;
41253 this.linode.setStyle("display", hidden ? "none" : "");
41257 * Returns true if this tab is "hidden"
41258 * @return {Boolean}
41260 isHidden : function(){
41261 return this.hidden;
41265 * Returns the text for this tab
41268 getText : function(){
41272 autoSize : function(){
41273 //this.el.beginMeasure();
41274 this.textEl.setWidth(1);
41276 * #2804 [new] Tabs in Roojs
41277 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41279 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41280 //this.el.endMeasure();
41284 * Sets the text for the tab (Note: this also sets the tooltip text)
41285 * @param {String} text The tab's text and tooltip
41287 setText : function(text){
41289 this.textEl.update(text);
41290 this.setTooltip(text);
41291 //if(!this.tabPanel.resizeTabs){
41292 // this.autoSize();
41296 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41298 activate : function(){
41299 this.tabPanel.activate(this.id);
41303 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41305 disable : function(){
41306 if(this.tabPanel.active != this){
41307 this.disabled = true;
41308 this.status_node.addClass("disabled");
41313 * Enables this TabPanelItem if it was previously disabled.
41315 enable : function(){
41316 this.disabled = false;
41317 this.status_node.removeClass("disabled");
41321 * Sets the content for this TabPanelItem.
41322 * @param {String} content The content
41323 * @param {Boolean} loadScripts true to look for and load scripts
41325 setContent : function(content, loadScripts){
41326 this.bodyEl.update(content, loadScripts);
41330 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41331 * @return {Roo.UpdateManager} The UpdateManager
41333 getUpdateManager : function(){
41334 return this.bodyEl.getUpdateManager();
41338 * Set a URL to be used to load the content for this TabPanelItem.
41339 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41340 * @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)
41341 * @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)
41342 * @return {Roo.UpdateManager} The UpdateManager
41344 setUrl : function(url, params, loadOnce){
41345 if(this.refreshDelegate){
41346 this.un('activate', this.refreshDelegate);
41348 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41349 this.on("activate", this.refreshDelegate);
41350 return this.bodyEl.getUpdateManager();
41354 _handleRefresh : function(url, params, loadOnce){
41355 if(!loadOnce || !this.loaded){
41356 var updater = this.bodyEl.getUpdateManager();
41357 updater.update(url, params, this._setLoaded.createDelegate(this));
41362 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41363 * Will fail silently if the setUrl method has not been called.
41364 * This does not activate the panel, just updates its content.
41366 refresh : function(){
41367 if(this.refreshDelegate){
41368 this.loaded = false;
41369 this.refreshDelegate();
41374 _setLoaded : function(){
41375 this.loaded = true;
41379 closeClick : function(e){
41382 this.fireEvent("beforeclose", this, o);
41383 if(o.cancel !== true){
41384 this.tabPanel.removeTab(this.id);
41388 * The text displayed in the tooltip for the close icon.
41391 closeText : "Close this tab"
41394 * This script refer to:
41395 * Title: International Telephone Input
41396 * Author: Jack O'Connor
41397 * Code version: v12.1.12
41398 * Availability: https://github.com/jackocnr/intl-tel-input.git
41401 Roo.bootstrap.PhoneInputData = function() {
41404 "Afghanistan (افغانستان)",
41409 "Albania (Shqipëri)",
41414 "Algeria (الجزائر)",
41439 "Antigua and Barbuda",
41449 "Armenia (Հայաստան)",
41465 "Austria (Österreich)",
41470 "Azerbaijan (Azərbaycan)",
41480 "Bahrain (البحرين)",
41485 "Bangladesh (বাংলাদেশ)",
41495 "Belarus (Беларусь)",
41500 "Belgium (België)",
41530 "Bosnia and Herzegovina (Босна и Херцеговина)",
41545 "British Indian Ocean Territory",
41550 "British Virgin Islands",
41560 "Bulgaria (България)",
41570 "Burundi (Uburundi)",
41575 "Cambodia (កម្ពុជា)",
41580 "Cameroon (Cameroun)",
41589 ["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"]
41592 "Cape Verde (Kabu Verdi)",
41597 "Caribbean Netherlands",
41608 "Central African Republic (République centrafricaine)",
41628 "Christmas Island",
41634 "Cocos (Keeling) Islands",
41645 "Comoros (جزر القمر)",
41650 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41655 "Congo (Republic) (Congo-Brazzaville)",
41675 "Croatia (Hrvatska)",
41696 "Czech Republic (Česká republika)",
41701 "Denmark (Danmark)",
41716 "Dominican Republic (República Dominicana)",
41720 ["809", "829", "849"]
41738 "Equatorial Guinea (Guinea Ecuatorial)",
41758 "Falkland Islands (Islas Malvinas)",
41763 "Faroe Islands (Føroyar)",
41784 "French Guiana (Guyane française)",
41789 "French Polynesia (Polynésie française)",
41804 "Georgia (საქართველო)",
41809 "Germany (Deutschland)",
41829 "Greenland (Kalaallit Nunaat)",
41866 "Guinea-Bissau (Guiné Bissau)",
41891 "Hungary (Magyarország)",
41896 "Iceland (Ísland)",
41916 "Iraq (العراق)",
41932 "Israel (ישראל)",
41959 "Jordan (الأردن)",
41964 "Kazakhstan (Казахстан)",
41985 "Kuwait (الكويت)",
41990 "Kyrgyzstan (Кыргызстан)",
42000 "Latvia (Latvija)",
42005 "Lebanon (لبنان)",
42020 "Libya (ليبيا)",
42030 "Lithuania (Lietuva)",
42045 "Macedonia (FYROM) (Македонија)",
42050 "Madagascar (Madagasikara)",
42080 "Marshall Islands",
42090 "Mauritania (موريتانيا)",
42095 "Mauritius (Moris)",
42116 "Moldova (Republica Moldova)",
42126 "Mongolia (Монгол)",
42131 "Montenegro (Crna Gora)",
42141 "Morocco (المغرب)",
42147 "Mozambique (Moçambique)",
42152 "Myanmar (Burma) (မြန်မာ)",
42157 "Namibia (Namibië)",
42172 "Netherlands (Nederland)",
42177 "New Caledonia (Nouvelle-Calédonie)",
42212 "North Korea (조선 민주주의 인민 공화국)",
42217 "Northern Mariana Islands",
42233 "Pakistan (پاکستان)",
42243 "Palestine (فلسطين)",
42253 "Papua New Guinea",
42295 "Réunion (La Réunion)",
42301 "Romania (România)",
42317 "Saint Barthélemy",
42328 "Saint Kitts and Nevis",
42338 "Saint Martin (Saint-Martin (partie française))",
42344 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42349 "Saint Vincent and the Grenadines",
42364 "São Tomé and Príncipe (São Tomé e Príncipe)",
42369 "Saudi Arabia (المملكة العربية السعودية)",
42374 "Senegal (Sénégal)",
42404 "Slovakia (Slovensko)",
42409 "Slovenia (Slovenija)",
42419 "Somalia (Soomaaliya)",
42429 "South Korea (대한민국)",
42434 "South Sudan (جنوب السودان)",
42444 "Sri Lanka (ශ්රී ලංකාව)",
42449 "Sudan (السودان)",
42459 "Svalbard and Jan Mayen",
42470 "Sweden (Sverige)",
42475 "Switzerland (Schweiz)",
42480 "Syria (سوريا)",
42525 "Trinidad and Tobago",
42530 "Tunisia (تونس)",
42535 "Turkey (Türkiye)",
42545 "Turks and Caicos Islands",
42555 "U.S. Virgin Islands",
42565 "Ukraine (Україна)",
42570 "United Arab Emirates (الإمارات العربية المتحدة)",
42592 "Uzbekistan (Oʻzbekiston)",
42602 "Vatican City (Città del Vaticano)",
42613 "Vietnam (Việt Nam)",
42618 "Wallis and Futuna (Wallis-et-Futuna)",
42623 "Western Sahara (الصحراء الغربية)",
42629 "Yemen (اليمن)",
42653 * This script refer to:
42654 * Title: International Telephone Input
42655 * Author: Jack O'Connor
42656 * Code version: v12.1.12
42657 * Availability: https://github.com/jackocnr/intl-tel-input.git
42661 * @class Roo.bootstrap.PhoneInput
42662 * @extends Roo.bootstrap.TriggerField
42663 * An input with International dial-code selection
42665 * @cfg {String} defaultDialCode default '+852'
42666 * @cfg {Array} preferedCountries default []
42669 * Create a new PhoneInput.
42670 * @param {Object} config Configuration options
42673 Roo.bootstrap.PhoneInput = function(config) {
42674 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42677 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42679 listWidth: undefined,
42681 selectedClass: 'active',
42683 invalidClass : "has-warning",
42685 validClass: 'has-success',
42687 allowed: '0123456789',
42692 * @cfg {String} defaultDialCode The default dial code when initializing the input
42694 defaultDialCode: '+852',
42697 * @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
42699 preferedCountries: false,
42701 getAutoCreate : function()
42703 var data = Roo.bootstrap.PhoneInputData();
42704 var align = this.labelAlign || this.parentLabelAlign();
42707 this.allCountries = [];
42708 this.dialCodeMapping = [];
42710 for (var i = 0; i < data.length; i++) {
42712 this.allCountries[i] = {
42716 priority: c[3] || 0,
42717 areaCodes: c[4] || null
42719 this.dialCodeMapping[c[2]] = {
42722 priority: c[3] || 0,
42723 areaCodes: c[4] || null
42735 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42736 maxlength: this.max_length,
42737 cls : 'form-control tel-input',
42738 autocomplete: 'new-password'
42741 var hiddenInput = {
42744 cls: 'hidden-tel-input'
42748 hiddenInput.name = this.name;
42751 if (this.disabled) {
42752 input.disabled = true;
42755 var flag_container = {
42772 cls: this.hasFeedback ? 'has-feedback' : '',
42778 cls: 'dial-code-holder',
42785 cls: 'roo-select2-container input-group',
42792 if (this.fieldLabel.length) {
42795 tooltip: 'This field is required'
42801 cls: 'control-label',
42807 html: this.fieldLabel
42810 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42816 if(this.indicatorpos == 'right') {
42817 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42824 if(align == 'left') {
42832 if(this.labelWidth > 12){
42833 label.style = "width: " + this.labelWidth + 'px';
42835 if(this.labelWidth < 13 && this.labelmd == 0){
42836 this.labelmd = this.labelWidth;
42838 if(this.labellg > 0){
42839 label.cls += ' col-lg-' + this.labellg;
42840 input.cls += ' col-lg-' + (12 - this.labellg);
42842 if(this.labelmd > 0){
42843 label.cls += ' col-md-' + this.labelmd;
42844 container.cls += ' col-md-' + (12 - this.labelmd);
42846 if(this.labelsm > 0){
42847 label.cls += ' col-sm-' + this.labelsm;
42848 container.cls += ' col-sm-' + (12 - this.labelsm);
42850 if(this.labelxs > 0){
42851 label.cls += ' col-xs-' + this.labelxs;
42852 container.cls += ' col-xs-' + (12 - this.labelxs);
42862 var settings = this;
42864 ['xs','sm','md','lg'].map(function(size){
42865 if (settings[size]) {
42866 cfg.cls += ' col-' + size + '-' + settings[size];
42870 this.store = new Roo.data.Store({
42871 proxy : new Roo.data.MemoryProxy({}),
42872 reader : new Roo.data.JsonReader({
42883 'name' : 'dialCode',
42887 'name' : 'priority',
42891 'name' : 'areaCodes',
42898 if(!this.preferedCountries) {
42899 this.preferedCountries = [
42906 var p = this.preferedCountries.reverse();
42909 for (var i = 0; i < p.length; i++) {
42910 for (var j = 0; j < this.allCountries.length; j++) {
42911 if(this.allCountries[j].iso2 == p[i]) {
42912 var t = this.allCountries[j];
42913 this.allCountries.splice(j,1);
42914 this.allCountries.unshift(t);
42920 this.store.proxy.data = {
42922 data: this.allCountries
42928 initEvents : function()
42931 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42933 this.indicator = this.indicatorEl();
42934 this.flag = this.flagEl();
42935 this.dialCodeHolder = this.dialCodeHolderEl();
42937 this.trigger = this.el.select('div.flag-box',true).first();
42938 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42943 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42944 _this.list.setWidth(lw);
42947 this.list.on('mouseover', this.onViewOver, this);
42948 this.list.on('mousemove', this.onViewMove, this);
42949 this.inputEl().on("keyup", this.onKeyUp, this);
42950 this.inputEl().on("keypress", this.onKeyPress, this);
42952 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42954 this.view = new Roo.View(this.list, this.tpl, {
42955 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42958 this.view.on('click', this.onViewClick, this);
42959 this.setValue(this.defaultDialCode);
42962 onTriggerClick : function(e)
42964 Roo.log('trigger click');
42969 if(this.isExpanded()){
42971 this.hasFocus = false;
42973 this.store.load({});
42974 this.hasFocus = true;
42979 isExpanded : function()
42981 return this.list.isVisible();
42984 collapse : function()
42986 if(!this.isExpanded()){
42990 Roo.get(document).un('mousedown', this.collapseIf, this);
42991 Roo.get(document).un('mousewheel', this.collapseIf, this);
42992 this.fireEvent('collapse', this);
42996 expand : function()
43000 if(this.isExpanded() || !this.hasFocus){
43004 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43005 this.list.setWidth(lw);
43008 this.restrictHeight();
43010 Roo.get(document).on('mousedown', this.collapseIf, this);
43011 Roo.get(document).on('mousewheel', this.collapseIf, this);
43013 this.fireEvent('expand', this);
43016 restrictHeight : function()
43018 this.list.alignTo(this.inputEl(), this.listAlign);
43019 this.list.alignTo(this.inputEl(), this.listAlign);
43022 onViewOver : function(e, t)
43024 if(this.inKeyMode){
43027 var item = this.view.findItemFromChild(t);
43030 var index = this.view.indexOf(item);
43031 this.select(index, false);
43036 onViewClick : function(view, doFocus, el, e)
43038 var index = this.view.getSelectedIndexes()[0];
43040 var r = this.store.getAt(index);
43043 this.onSelect(r, index);
43045 if(doFocus !== false && !this.blockFocus){
43046 this.inputEl().focus();
43050 onViewMove : function(e, t)
43052 this.inKeyMode = false;
43055 select : function(index, scrollIntoView)
43057 this.selectedIndex = index;
43058 this.view.select(index);
43059 if(scrollIntoView !== false){
43060 var el = this.view.getNode(index);
43062 this.list.scrollChildIntoView(el, false);
43067 createList : function()
43069 this.list = Roo.get(document.body).createChild({
43071 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43072 style: 'display:none'
43075 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43078 collapseIf : function(e)
43080 var in_combo = e.within(this.el);
43081 var in_list = e.within(this.list);
43082 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43084 if (in_combo || in_list || is_list) {
43090 onSelect : function(record, index)
43092 if(this.fireEvent('beforeselect', this, record, index) !== false){
43094 this.setFlagClass(record.data.iso2);
43095 this.setDialCode(record.data.dialCode);
43096 this.hasFocus = false;
43098 this.fireEvent('select', this, record, index);
43102 flagEl : function()
43104 var flag = this.el.select('div.flag',true).first();
43111 dialCodeHolderEl : function()
43113 var d = this.el.select('input.dial-code-holder',true).first();
43120 setDialCode : function(v)
43122 this.dialCodeHolder.dom.value = '+'+v;
43125 setFlagClass : function(n)
43127 this.flag.dom.className = 'flag '+n;
43130 getValue : function()
43132 var v = this.inputEl().getValue();
43133 if(this.dialCodeHolder) {
43134 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43139 setValue : function(v)
43141 var d = this.getDialCode(v);
43143 //invalid dial code
43144 if(v.length == 0 || !d || d.length == 0) {
43146 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43147 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43153 this.setFlagClass(this.dialCodeMapping[d].iso2);
43154 this.setDialCode(d);
43155 this.inputEl().dom.value = v.replace('+'+d,'');
43156 this.hiddenEl().dom.value = this.getValue();
43161 getDialCode : function(v)
43165 if (v.length == 0) {
43166 return this.dialCodeHolder.dom.value;
43170 if (v.charAt(0) != "+") {
43173 var numericChars = "";
43174 for (var i = 1; i < v.length; i++) {
43175 var c = v.charAt(i);
43178 if (this.dialCodeMapping[numericChars]) {
43179 dialCode = v.substr(1, i);
43181 if (numericChars.length == 4) {
43191 this.setValue(this.defaultDialCode);
43195 hiddenEl : function()
43197 return this.el.select('input.hidden-tel-input',true).first();
43200 // after setting val
43201 onKeyUp : function(e){
43202 this.setValue(this.getValue());
43205 onKeyPress : function(e){
43206 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43213 * @class Roo.bootstrap.MoneyField
43214 * @extends Roo.bootstrap.ComboBox
43215 * Bootstrap MoneyField class
43218 * Create a new MoneyField.
43219 * @param {Object} config Configuration options
43222 Roo.bootstrap.MoneyField = function(config) {
43224 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43228 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43231 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43233 allowDecimals : true,
43235 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43237 decimalSeparator : ".",
43239 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43241 decimalPrecision : 0,
43243 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43245 allowNegative : true,
43247 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43251 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43253 minValue : Number.NEGATIVE_INFINITY,
43255 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43257 maxValue : Number.MAX_VALUE,
43259 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43261 minText : "The minimum value for this field is {0}",
43263 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43265 maxText : "The maximum value for this field is {0}",
43267 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43268 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43270 nanText : "{0} is not a valid number",
43272 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43276 * @cfg {String} defaults currency of the MoneyField
43277 * value should be in lkey
43279 defaultCurrency : false,
43281 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43283 thousandsDelimiter : false,
43285 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43296 getAutoCreate : function()
43298 var align = this.labelAlign || this.parentLabelAlign();
43310 cls : 'form-control roo-money-amount-input',
43311 autocomplete: 'new-password'
43314 var hiddenInput = {
43318 cls: 'hidden-number-input'
43321 if(this.max_length) {
43322 input.maxlength = this.max_length;
43326 hiddenInput.name = this.name;
43329 if (this.disabled) {
43330 input.disabled = true;
43333 var clg = 12 - this.inputlg;
43334 var cmd = 12 - this.inputmd;
43335 var csm = 12 - this.inputsm;
43336 var cxs = 12 - this.inputxs;
43340 cls : 'row roo-money-field',
43344 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43348 cls: 'roo-select2-container input-group',
43352 cls : 'form-control roo-money-currency-input',
43353 autocomplete: 'new-password',
43355 name : this.currencyName
43359 cls : 'input-group-addon',
43373 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43377 cls: this.hasFeedback ? 'has-feedback' : '',
43388 if (this.fieldLabel.length) {
43391 tooltip: 'This field is required'
43397 cls: 'control-label',
43403 html: this.fieldLabel
43406 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43412 if(this.indicatorpos == 'right') {
43413 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43420 if(align == 'left') {
43428 if(this.labelWidth > 12){
43429 label.style = "width: " + this.labelWidth + 'px';
43431 if(this.labelWidth < 13 && this.labelmd == 0){
43432 this.labelmd = this.labelWidth;
43434 if(this.labellg > 0){
43435 label.cls += ' col-lg-' + this.labellg;
43436 input.cls += ' col-lg-' + (12 - this.labellg);
43438 if(this.labelmd > 0){
43439 label.cls += ' col-md-' + this.labelmd;
43440 container.cls += ' col-md-' + (12 - this.labelmd);
43442 if(this.labelsm > 0){
43443 label.cls += ' col-sm-' + this.labelsm;
43444 container.cls += ' col-sm-' + (12 - this.labelsm);
43446 if(this.labelxs > 0){
43447 label.cls += ' col-xs-' + this.labelxs;
43448 container.cls += ' col-xs-' + (12 - this.labelxs);
43459 var settings = this;
43461 ['xs','sm','md','lg'].map(function(size){
43462 if (settings[size]) {
43463 cfg.cls += ' col-' + size + '-' + settings[size];
43470 initEvents : function()
43472 this.indicator = this.indicatorEl();
43474 this.initCurrencyEvent();
43476 this.initNumberEvent();
43479 initCurrencyEvent : function()
43482 throw "can not find store for combo";
43485 this.store = Roo.factory(this.store, Roo.data);
43486 this.store.parent = this;
43490 this.triggerEl = this.el.select('.input-group-addon', true).first();
43492 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43497 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43498 _this.list.setWidth(lw);
43501 this.list.on('mouseover', this.onViewOver, this);
43502 this.list.on('mousemove', this.onViewMove, this);
43503 this.list.on('scroll', this.onViewScroll, this);
43506 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43509 this.view = new Roo.View(this.list, this.tpl, {
43510 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43513 this.view.on('click', this.onViewClick, this);
43515 this.store.on('beforeload', this.onBeforeLoad, this);
43516 this.store.on('load', this.onLoad, this);
43517 this.store.on('loadexception', this.onLoadException, this);
43519 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43520 "up" : function(e){
43521 this.inKeyMode = true;
43525 "down" : function(e){
43526 if(!this.isExpanded()){
43527 this.onTriggerClick();
43529 this.inKeyMode = true;
43534 "enter" : function(e){
43537 if(this.fireEvent("specialkey", this, e)){
43538 this.onViewClick(false);
43544 "esc" : function(e){
43548 "tab" : function(e){
43551 if(this.fireEvent("specialkey", this, e)){
43552 this.onViewClick(false);
43560 doRelay : function(foo, bar, hname){
43561 if(hname == 'down' || this.scope.isExpanded()){
43562 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43570 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43574 initNumberEvent : function(e)
43576 this.inputEl().on("keydown" , this.fireKey, this);
43577 this.inputEl().on("focus", this.onFocus, this);
43578 this.inputEl().on("blur", this.onBlur, this);
43580 this.inputEl().relayEvent('keyup', this);
43582 if(this.indicator){
43583 this.indicator.addClass('invisible');
43586 this.originalValue = this.getValue();
43588 if(this.validationEvent == 'keyup'){
43589 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43590 this.inputEl().on('keyup', this.filterValidation, this);
43592 else if(this.validationEvent !== false){
43593 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43596 if(this.selectOnFocus){
43597 this.on("focus", this.preFocus, this);
43600 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43601 this.inputEl().on("keypress", this.filterKeys, this);
43603 this.inputEl().relayEvent('keypress', this);
43606 var allowed = "0123456789";
43608 if(this.allowDecimals){
43609 allowed += this.decimalSeparator;
43612 if(this.allowNegative){
43616 if(this.thousandsDelimiter) {
43620 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43622 var keyPress = function(e){
43624 var k = e.getKey();
43626 var c = e.getCharCode();
43629 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43630 allowed.indexOf(String.fromCharCode(c)) === -1
43636 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43640 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43645 this.inputEl().on("keypress", keyPress, this);
43649 onTriggerClick : function(e)
43656 this.loadNext = false;
43658 if(this.isExpanded()){
43663 this.hasFocus = true;
43665 if(this.triggerAction == 'all') {
43666 this.doQuery(this.allQuery, true);
43670 this.doQuery(this.getRawValue());
43673 getCurrency : function()
43675 var v = this.currencyEl().getValue();
43680 restrictHeight : function()
43682 this.list.alignTo(this.currencyEl(), this.listAlign);
43683 this.list.alignTo(this.currencyEl(), this.listAlign);
43686 onViewClick : function(view, doFocus, el, e)
43688 var index = this.view.getSelectedIndexes()[0];
43690 var r = this.store.getAt(index);
43693 this.onSelect(r, index);
43697 onSelect : function(record, index){
43699 if(this.fireEvent('beforeselect', this, record, index) !== false){
43701 this.setFromCurrencyData(index > -1 ? record.data : false);
43705 this.fireEvent('select', this, record, index);
43709 setFromCurrencyData : function(o)
43713 this.lastCurrency = o;
43715 if (this.currencyField) {
43716 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43718 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43721 this.lastSelectionText = currency;
43723 //setting default currency
43724 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43725 this.setCurrency(this.defaultCurrency);
43729 this.setCurrency(currency);
43732 setFromData : function(o)
43736 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43738 this.setFromCurrencyData(c);
43743 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43745 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43748 this.setValue(value);
43752 setCurrency : function(v)
43754 this.currencyValue = v;
43757 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43762 setValue : function(v)
43764 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43770 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43772 this.inputEl().dom.value = (v == '') ? '' :
43773 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43775 if(!this.allowZero && v === '0') {
43776 this.hiddenEl().dom.value = '';
43777 this.inputEl().dom.value = '';
43784 getRawValue : function()
43786 var v = this.inputEl().getValue();
43791 getValue : function()
43793 return this.fixPrecision(this.parseValue(this.getRawValue()));
43796 parseValue : function(value)
43798 if(this.thousandsDelimiter) {
43800 r = new RegExp(",", "g");
43801 value = value.replace(r, "");
43804 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43805 return isNaN(value) ? '' : value;
43809 fixPrecision : function(value)
43811 if(this.thousandsDelimiter) {
43813 r = new RegExp(",", "g");
43814 value = value.replace(r, "");
43817 var nan = isNaN(value);
43819 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43820 return nan ? '' : value;
43822 return parseFloat(value).toFixed(this.decimalPrecision);
43825 decimalPrecisionFcn : function(v)
43827 return Math.floor(v);
43830 validateValue : function(value)
43832 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43836 var num = this.parseValue(value);
43839 this.markInvalid(String.format(this.nanText, value));
43843 if(num < this.minValue){
43844 this.markInvalid(String.format(this.minText, this.minValue));
43848 if(num > this.maxValue){
43849 this.markInvalid(String.format(this.maxText, this.maxValue));
43856 validate : function()
43858 if(this.disabled || this.allowBlank){
43863 var currency = this.getCurrency();
43865 if(this.validateValue(this.getRawValue()) && currency.length){
43870 this.markInvalid();
43874 getName: function()
43879 beforeBlur : function()
43885 var v = this.parseValue(this.getRawValue());
43892 onBlur : function()
43896 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43897 //this.el.removeClass(this.focusClass);
43900 this.hasFocus = false;
43902 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43906 var v = this.getValue();
43908 if(String(v) !== String(this.startValue)){
43909 this.fireEvent('change', this, v, this.startValue);
43912 this.fireEvent("blur", this);
43915 inputEl : function()
43917 return this.el.select('.roo-money-amount-input', true).first();
43920 currencyEl : function()
43922 return this.el.select('.roo-money-currency-input', true).first();
43925 hiddenEl : function()
43927 return this.el.select('input.hidden-number-input',true).first();
43931 * @class Roo.bootstrap.BezierSignature
43932 * @extends Roo.bootstrap.Component
43933 * Bootstrap BezierSignature class
43934 * This script refer to:
43935 * Title: Signature Pad
43937 * Availability: https://github.com/szimek/signature_pad
43940 * Create a new BezierSignature
43941 * @param {Object} config The config object
43944 Roo.bootstrap.BezierSignature = function(config){
43945 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43951 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43958 mouse_btn_down: true,
43961 * @cfg {int} canvas height
43963 canvas_height: '200px',
43966 * @cfg {float|function} Radius of a single dot.
43971 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43976 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43981 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43986 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43991 * @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.
43993 bg_color: 'rgba(0, 0, 0, 0)',
43996 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43998 dot_color: 'black',
44001 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44003 velocity_filter_weight: 0.7,
44006 * @cfg {function} Callback when stroke begin.
44011 * @cfg {function} Callback when stroke end.
44015 getAutoCreate : function()
44017 var cls = 'roo-signature column';
44020 cls += ' ' + this.cls;
44030 for(var i = 0; i < col_sizes.length; i++) {
44031 if(this[col_sizes[i]]) {
44032 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44042 cls: 'roo-signature-body',
44046 cls: 'roo-signature-body-canvas',
44047 height: this.canvas_height,
44048 width: this.canvas_width
44055 style: 'display: none'
44063 initEvents: function()
44065 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44067 var canvas = this.canvasEl();
44069 // mouse && touch event swapping...
44070 canvas.dom.style.touchAction = 'none';
44071 canvas.dom.style.msTouchAction = 'none';
44073 this.mouse_btn_down = false;
44074 canvas.on('mousedown', this._handleMouseDown, this);
44075 canvas.on('mousemove', this._handleMouseMove, this);
44076 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44078 if (window.PointerEvent) {
44079 canvas.on('pointerdown', this._handleMouseDown, this);
44080 canvas.on('pointermove', this._handleMouseMove, this);
44081 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44084 if ('ontouchstart' in window) {
44085 canvas.on('touchstart', this._handleTouchStart, this);
44086 canvas.on('touchmove', this._handleTouchMove, this);
44087 canvas.on('touchend', this._handleTouchEnd, this);
44090 Roo.EventManager.onWindowResize(this.resize, this, true);
44092 // file input event
44093 this.fileEl().on('change', this.uploadImage, this);
44100 resize: function(){
44102 var canvas = this.canvasEl().dom;
44103 var ctx = this.canvasElCtx();
44104 var img_data = false;
44106 if(canvas.width > 0) {
44107 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44109 // setting canvas width will clean img data
44112 var style = window.getComputedStyle ?
44113 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44115 var padding_left = parseInt(style.paddingLeft) || 0;
44116 var padding_right = parseInt(style.paddingRight) || 0;
44118 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44121 ctx.putImageData(img_data, 0, 0);
44125 _handleMouseDown: function(e)
44127 if (e.browserEvent.which === 1) {
44128 this.mouse_btn_down = true;
44129 this.strokeBegin(e);
44133 _handleMouseMove: function (e)
44135 if (this.mouse_btn_down) {
44136 this.strokeMoveUpdate(e);
44140 _handleMouseUp: function (e)
44142 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44143 this.mouse_btn_down = false;
44148 _handleTouchStart: function (e) {
44150 e.preventDefault();
44151 if (e.browserEvent.targetTouches.length === 1) {
44152 // var touch = e.browserEvent.changedTouches[0];
44153 // this.strokeBegin(touch);
44155 this.strokeBegin(e); // assume e catching the correct xy...
44159 _handleTouchMove: function (e) {
44160 e.preventDefault();
44161 // var touch = event.targetTouches[0];
44162 // _this._strokeMoveUpdate(touch);
44163 this.strokeMoveUpdate(e);
44166 _handleTouchEnd: function (e) {
44167 var wasCanvasTouched = e.target === this.canvasEl().dom;
44168 if (wasCanvasTouched) {
44169 e.preventDefault();
44170 // var touch = event.changedTouches[0];
44171 // _this._strokeEnd(touch);
44176 reset: function () {
44177 this._lastPoints = [];
44178 this._lastVelocity = 0;
44179 this._lastWidth = (this.min_width + this.max_width) / 2;
44180 this.canvasElCtx().fillStyle = this.dot_color;
44183 strokeMoveUpdate: function(e)
44185 this.strokeUpdate(e);
44187 if (this.throttle) {
44188 this.throttleStroke(this.strokeUpdate, this.throttle);
44191 this.strokeUpdate(e);
44195 strokeBegin: function(e)
44197 var newPointGroup = {
44198 color: this.dot_color,
44202 if (typeof this.onBegin === 'function') {
44206 this.curve_data.push(newPointGroup);
44208 this.strokeUpdate(e);
44211 strokeUpdate: function(e)
44213 var rect = this.canvasEl().dom.getBoundingClientRect();
44214 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44215 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44216 var lastPoints = lastPointGroup.points;
44217 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44218 var isLastPointTooClose = lastPoint
44219 ? point.distanceTo(lastPoint) <= this.min_distance
44221 var color = lastPointGroup.color;
44222 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44223 var curve = this.addPoint(point);
44225 this.drawDot({color: color, point: point});
44228 this.drawCurve({color: color, curve: curve});
44238 strokeEnd: function(e)
44240 this.strokeUpdate(e);
44241 if (typeof this.onEnd === 'function') {
44246 addPoint: function (point) {
44247 var _lastPoints = this._lastPoints;
44248 _lastPoints.push(point);
44249 if (_lastPoints.length > 2) {
44250 if (_lastPoints.length === 3) {
44251 _lastPoints.unshift(_lastPoints[0]);
44253 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44254 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44255 _lastPoints.shift();
44261 calculateCurveWidths: function (startPoint, endPoint) {
44262 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44263 (1 - this.velocity_filter_weight) * this._lastVelocity;
44265 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44268 start: this._lastWidth
44271 this._lastVelocity = velocity;
44272 this._lastWidth = newWidth;
44276 drawDot: function (_a) {
44277 var color = _a.color, point = _a.point;
44278 var ctx = this.canvasElCtx();
44279 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44281 this.drawCurveSegment(point.x, point.y, width);
44283 ctx.fillStyle = color;
44287 drawCurve: function (_a) {
44288 var color = _a.color, curve = _a.curve;
44289 var ctx = this.canvasElCtx();
44290 var widthDelta = curve.endWidth - curve.startWidth;
44291 var drawSteps = Math.floor(curve.length()) * 2;
44293 ctx.fillStyle = color;
44294 for (var i = 0; i < drawSteps; i += 1) {
44295 var t = i / drawSteps;
44301 var x = uuu * curve.startPoint.x;
44302 x += 3 * uu * t * curve.control1.x;
44303 x += 3 * u * tt * curve.control2.x;
44304 x += ttt * curve.endPoint.x;
44305 var y = uuu * curve.startPoint.y;
44306 y += 3 * uu * t * curve.control1.y;
44307 y += 3 * u * tt * curve.control2.y;
44308 y += ttt * curve.endPoint.y;
44309 var width = curve.startWidth + ttt * widthDelta;
44310 this.drawCurveSegment(x, y, width);
44316 drawCurveSegment: function (x, y, width) {
44317 var ctx = this.canvasElCtx();
44319 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44320 this.is_empty = false;
44325 var ctx = this.canvasElCtx();
44326 var canvas = this.canvasEl().dom;
44327 ctx.fillStyle = this.bg_color;
44328 ctx.clearRect(0, 0, canvas.width, canvas.height);
44329 ctx.fillRect(0, 0, canvas.width, canvas.height);
44330 this.curve_data = [];
44332 this.is_empty = true;
44337 return this.el.select('input',true).first();
44340 canvasEl: function()
44342 return this.el.select('canvas',true).first();
44345 canvasElCtx: function()
44347 return this.el.select('canvas',true).first().dom.getContext('2d');
44350 getImage: function(type)
44352 if(this.is_empty) {
44357 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44360 drawFromImage: function(img_src)
44362 var img = new Image();
44364 img.onload = function(){
44365 this.canvasElCtx().drawImage(img, 0, 0);
44370 this.is_empty = false;
44373 selectImage: function()
44375 this.fileEl().dom.click();
44378 uploadImage: function(e)
44380 var reader = new FileReader();
44382 reader.onload = function(e){
44383 var img = new Image();
44384 img.onload = function(){
44386 this.canvasElCtx().drawImage(img, 0, 0);
44388 img.src = e.target.result;
44391 reader.readAsDataURL(e.target.files[0]);
44394 // Bezier Point Constructor
44395 Point: (function () {
44396 function Point(x, y, time) {
44399 this.time = time || Date.now();
44401 Point.prototype.distanceTo = function (start) {
44402 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44404 Point.prototype.equals = function (other) {
44405 return this.x === other.x && this.y === other.y && this.time === other.time;
44407 Point.prototype.velocityFrom = function (start) {
44408 return this.time !== start.time
44409 ? this.distanceTo(start) / (this.time - start.time)
44416 // Bezier Constructor
44417 Bezier: (function () {
44418 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44419 this.startPoint = startPoint;
44420 this.control2 = control2;
44421 this.control1 = control1;
44422 this.endPoint = endPoint;
44423 this.startWidth = startWidth;
44424 this.endWidth = endWidth;
44426 Bezier.fromPoints = function (points, widths, scope) {
44427 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44428 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44429 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44431 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44432 var dx1 = s1.x - s2.x;
44433 var dy1 = s1.y - s2.y;
44434 var dx2 = s2.x - s3.x;
44435 var dy2 = s2.y - s3.y;
44436 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44437 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44438 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44439 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44440 var dxm = m1.x - m2.x;
44441 var dym = m1.y - m2.y;
44442 var k = l2 / (l1 + l2);
44443 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44444 var tx = s2.x - cm.x;
44445 var ty = s2.y - cm.y;
44447 c1: new scope.Point(m1.x + tx, m1.y + ty),
44448 c2: new scope.Point(m2.x + tx, m2.y + ty)
44451 Bezier.prototype.length = function () {
44456 for (var i = 0; i <= steps; i += 1) {
44458 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44459 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44461 var xdiff = cx - px;
44462 var ydiff = cy - py;
44463 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44470 Bezier.prototype.point = function (t, start, c1, c2, end) {
44471 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44472 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44473 + (3.0 * c2 * (1.0 - t) * t * t)
44474 + (end * t * t * t);
44479 throttleStroke: function(fn, wait) {
44480 if (wait === void 0) { wait = 250; }
44482 var timeout = null;
44486 var later = function () {
44487 previous = Date.now();
44489 result = fn.apply(storedContext, storedArgs);
44491 storedContext = null;
44495 return function wrapper() {
44497 for (var _i = 0; _i < arguments.length; _i++) {
44498 args[_i] = arguments[_i];
44500 var now = Date.now();
44501 var remaining = wait - (now - previous);
44502 storedContext = this;
44504 if (remaining <= 0 || remaining > wait) {
44506 clearTimeout(timeout);
44510 result = fn.apply(storedContext, storedArgs);
44512 storedContext = null;
44516 else if (!timeout) {
44517 timeout = window.setTimeout(later, remaining);