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)
100 * @cfg {String} offset
101 * The number of pixels to offset the shadow from the element (defaults to 4)
109 * Displays the shadow under the target element
110 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
112 show : function(target){
113 target = Roo.get(target);
115 this.el = Roo.Shadow.Pool.pull();
116 if(this.el.dom.nextSibling != target.dom){
117 this.el.insertBefore(target);
120 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
122 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
125 target.getLeft(true),
130 this.el.dom.style.display = "block";
134 * Returns true if the shadow is visible, else false
136 isVisible : function(){
137 return this.el ? true : false;
141 * Direct alignment when values are already available. Show must be called at least once before
142 * calling this method to ensure it is initialized.
143 * @param {Number} left The target element left position
144 * @param {Number} top The target element top position
145 * @param {Number} width The target element width
146 * @param {Number} height The target element height
148 realign : function(l, t, w, h){
152 var a = this.adjusts, d = this.el.dom, s = d.style;
154 s.left = (l+a.l)+"px";
155 s.top = (t+a.t)+"px";
156 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
158 if(s.width != sws || s.height != shs){
162 var cn = d.childNodes;
163 var sww = Math.max(0, (sw-12))+"px";
164 cn[0].childNodes[1].style.width = sww;
165 cn[1].childNodes[1].style.width = sww;
166 cn[2].childNodes[1].style.width = sww;
167 cn[1].style.height = Math.max(0, (sh-12))+"px";
177 this.el.dom.style.display = "none";
178 Roo.Shadow.Pool.push(this.el);
184 * Adjust the z-index of this shadow
185 * @param {Number} zindex The new z-index
187 setZIndex : function(z){
190 this.el.setStyle("z-index", z);
195 // Private utility class that manages the internal Shadow cache
196 Roo.Shadow.Pool = function(){
198 var markup = Roo.isIE ?
199 '<div class="x-ie-shadow"></div>' :
200 '<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>';
205 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
206 sh.autoBoxAdjust = false;
218 * base class for bootstrap elements.
222 Roo.bootstrap = Roo.bootstrap || {};
224 * @class Roo.bootstrap.Component
225 * @extends Roo.Component
227 * @children Roo.bootstrap.Component
228 * Bootstrap Component base class
229 * @cfg {String} cls css class
230 * @cfg {String} style any extra css
231 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
232 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
233 * @cfg {string} dataId cutomer id
234 * @cfg {string} name Specifies name attribute
235 * @cfg {string} tooltip Text for the tooltip
236 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
237 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
240 * Do not use directly - it does not do anything..
241 * @param {Object} config The config object
246 Roo.bootstrap.Component = function(config){
247 Roo.bootstrap.Component.superclass.constructor.call(this, config);
251 * @event childrenrendered
252 * Fires when the children have been rendered..
253 * @param {Roo.bootstrap.Component} this
255 "childrenrendered" : true
264 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
267 allowDomMove : false, // to stop relocations in parent onRender...
277 * Initialize Events for the element
279 initEvents : function() { },
285 can_build_overlaid : true,
287 container_method : false,
294 // returns the parent component..
295 return Roo.ComponentMgr.get(this.parentId)
301 onRender : function(ct, position)
303 // Roo.log("Call onRender: " + this.xtype);
305 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
308 if (this.el.attr('xtype')) {
309 this.el.attr('xtypex', this.el.attr('xtype'));
310 this.el.dom.removeAttribute('xtype');
320 var cfg = Roo.apply({}, this.getAutoCreate());
322 cfg.id = this.id || Roo.id();
324 // fill in the extra attributes
325 if (this.xattr && typeof(this.xattr) =='object') {
326 for (var i in this.xattr) {
327 cfg[i] = this.xattr[i];
332 cfg.dataId = this.dataId;
336 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
339 if (this.style) { // fixme needs to support more complex style data.
340 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
344 cfg.name = this.name;
347 this.el = ct.createChild(cfg, position);
350 this.tooltipEl().attr('tooltip', this.tooltip);
353 if(this.tabIndex !== undefined){
354 this.el.dom.setAttribute('tabIndex', this.tabIndex);
361 * Fetch the element to add children to
362 * @return {Roo.Element} defaults to this.el
364 getChildContainer : function()
368 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
370 return Roo.get(document.body);
374 * Fetch the element to display the tooltip on.
375 * @return {Roo.Element} defaults to this.el
377 tooltipEl : function()
382 addxtype : function(tree,cntr)
386 cn = Roo.factory(tree);
387 //Roo.log(['addxtype', cn]);
389 cn.parentType = this.xtype; //??
390 cn.parentId = this.id;
392 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
393 if (typeof(cn.container_method) == 'string') {
394 cntr = cn.container_method;
398 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
400 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
402 var build_from_html = Roo.XComponent.build_from_html;
404 var is_body = (tree.xtype == 'Body') ;
406 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
408 var self_cntr_el = Roo.get(this[cntr](false));
410 // do not try and build conditional elements
411 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
415 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
416 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
417 return this.addxtypeChild(tree,cntr, is_body);
420 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
423 return this.addxtypeChild(Roo.apply({}, tree),cntr);
426 Roo.log('skipping render');
432 if (!build_from_html) {
436 // this i think handles overlaying multiple children of the same type
437 // with the sam eelement.. - which might be buggy..
439 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
445 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
449 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
456 addxtypeChild : function (tree, cntr, is_body)
458 Roo.debug && Roo.log('addxtypeChild:' + cntr);
460 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
463 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
464 (typeof(tree['flexy:foreach']) != 'undefined');
468 skip_children = false;
469 // render the element if it's not BODY.
472 // if parent was disabled, then do not try and create the children..
473 if(!this[cntr](true)){
478 cn = Roo.factory(tree);
480 cn.parentType = this.xtype; //??
481 cn.parentId = this.id;
483 var build_from_html = Roo.XComponent.build_from_html;
486 // does the container contain child eleemnts with 'xtype' attributes.
487 // that match this xtype..
488 // note - when we render we create these as well..
489 // so we should check to see if body has xtype set.
490 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
492 var self_cntr_el = Roo.get(this[cntr](false));
493 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
495 //Roo.log(Roo.XComponent.build_from_html);
496 //Roo.log("got echild:");
499 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
500 // and are not displayed -this causes this to use up the wrong element when matching.
501 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
504 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
505 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
511 //echild.dom.removeAttribute('xtype');
513 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
514 Roo.debug && Roo.log(self_cntr_el);
515 Roo.debug && Roo.log(echild);
516 Roo.debug && Roo.log(cn);
522 // if object has flexy:if - then it may or may not be rendered.
523 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
524 // skip a flexy if element.
525 Roo.debug && Roo.log('skipping render');
526 Roo.debug && Roo.log(tree);
528 Roo.debug && Roo.log('skipping all children');
529 skip_children = true;
534 // actually if flexy:foreach is found, we really want to create
535 // multiple copies here...
537 //Roo.log(this[cntr]());
538 // some elements do not have render methods.. like the layouts...
540 if(this[cntr](true) === false){
545 cn.render && cn.render(this[cntr](true));
548 // then add the element..
555 if (typeof (tree.menu) != 'undefined') {
556 tree.menu.parentType = cn.xtype;
557 tree.menu.triggerEl = cn.el;
558 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
562 if (!tree.items || !tree.items.length) {
564 //Roo.log(["no children", this]);
569 var items = tree.items;
572 //Roo.log(items.length);
574 if (!skip_children) {
575 for(var i =0;i < items.length;i++) {
576 // Roo.log(['add child', items[i]]);
577 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
583 //Roo.log("fire childrenrendered");
585 cn.fireEvent('childrenrendered', this);
591 * Set the element that will be used to show or hide
593 setVisibilityEl : function(el)
595 this.visibilityEl = el;
599 * Get the element that will be used to show or hide
601 getVisibilityEl : function()
603 if (typeof(this.visibilityEl) == 'object') {
604 return this.visibilityEl;
607 if (typeof(this.visibilityEl) == 'string') {
608 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
615 * Show a component - removes 'hidden' class
619 if(!this.getVisibilityEl()){
623 this.getVisibilityEl().removeClass(['hidden','d-none']);
625 this.fireEvent('show', this);
630 * Hide a component - adds 'hidden' class
634 if(!this.getVisibilityEl()){
638 this.getVisibilityEl().addClass(['hidden','d-none']);
640 this.fireEvent('hide', this);
653 * @class Roo.bootstrap.Element
654 * @extends Roo.bootstrap.Component
655 * Bootstrap Element class
656 * @cfg {String} html contents of the element
657 * @cfg {String} tag tag of the element
658 * @cfg {String} cls class of the element
659 * @cfg {Boolean} preventDefault (true|false) default false
660 * @cfg {Boolean} clickable (true|false) default false
661 * @cfg {String} role default blank - set to button to force cursor pointer
665 * Create a new Element
666 * @param {Object} config The config object
669 Roo.bootstrap.Element = function(config){
670 Roo.bootstrap.Element.superclass.constructor.call(this, config);
676 * When a element is chick
677 * @param {Roo.bootstrap.Element} this
678 * @param {Roo.EventObject} e
686 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
691 preventDefault: false,
696 getAutoCreate : function(){
700 // cls: this.cls, double assign in parent class Component.js :: onRender
703 if (this.role !== false) {
704 cfg.role = this.role;
710 initEvents: function()
712 Roo.bootstrap.Element.superclass.initEvents.call(this);
715 this.el.on('click', this.onClick, this);
721 onClick : function(e)
723 if(this.preventDefault){
727 this.fireEvent('click', this, e); // why was this double click before?
735 getValue : function()
737 return this.el.dom.innerHTML;
740 setValue : function(value)
742 this.el.dom.innerHTML = value;
757 * @class Roo.bootstrap.DropTarget
758 * @extends Roo.bootstrap.Element
759 * Bootstrap DropTarget class
761 * @cfg {string} name dropable name
764 * Create a new Dropable Area
765 * @param {Object} config The config object
768 Roo.bootstrap.DropTarget = function(config){
769 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
775 * When a element is chick
776 * @param {Roo.bootstrap.Element} this
777 * @param {Roo.EventObject} e
783 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
786 getAutoCreate : function(){
791 initEvents: function()
793 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
794 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
797 drop : this.dragDrop.createDelegate(this),
798 enter : this.dragEnter.createDelegate(this),
799 out : this.dragOut.createDelegate(this),
800 over : this.dragOver.createDelegate(this)
804 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
807 dragDrop : function(source,e,data)
809 // user has to decide how to impliment this.
812 //this.fireEvent('drop', this, source, e ,data);
816 dragEnter : function(n, dd, e, data)
818 // probably want to resize the element to match the dropped element..
820 this.originalSize = this.el.getSize();
821 this.el.setSize( n.el.getSize());
822 this.dropZone.DDM.refreshCache(this.name);
823 Roo.log([n, dd, e, data]);
826 dragOut : function(value)
828 // resize back to normal
830 this.el.setSize(this.originalSize);
831 this.dropZone.resetConstraints();
834 dragOver : function()
851 * @class Roo.bootstrap.Body
852 * @extends Roo.bootstrap.Component
854 * @children Roo.bootstrap.Component
856 * Bootstrap Body class
860 * @param {Object} config The config object
863 Roo.bootstrap.Body = function(config){
865 config = config || {};
867 Roo.bootstrap.Body.superclass.constructor.call(this, config);
868 this.el = Roo.get(config.el ? config.el : document.body );
869 if (this.cls && this.cls.length) {
870 Roo.get(document.body).addClass(this.cls);
874 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
876 is_body : true,// just to make sure it's constructed?
881 onRender : function(ct, position)
883 /* Roo.log("Roo.bootstrap.Body - onRender");
884 if (this.cls && this.cls.length) {
885 Roo.get(document.body).addClass(this.cls);
904 * @class Roo.bootstrap.ButtonGroup
905 * @extends Roo.bootstrap.Component
906 * Bootstrap ButtonGroup class
907 * @cfg {String} size lg | sm | xs (default empty normal)
908 * @cfg {String} align vertical | justified (default none)
909 * @cfg {String} direction up | down (default down)
910 * @cfg {Boolean} toolbar false | true
911 * @cfg {Boolean} btn true | false
916 * @param {Object} config The config object
919 Roo.bootstrap.ButtonGroup = function(config){
920 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
923 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
931 getAutoCreate : function(){
937 cfg.html = this.html || cfg.html;
948 if (['vertical','justified'].indexOf(this.align)!==-1) {
949 cfg.cls = 'btn-group-' + this.align;
951 if (this.align == 'justified') {
952 console.log(this.items);
956 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
957 cfg.cls += ' btn-group-' + this.size;
960 if (this.direction == 'up') {
961 cfg.cls += ' dropup' ;
967 * Add a button to the group (similar to NavItem API.)
969 addItem : function(cfg)
971 var cn = new Roo.bootstrap.Button(cfg);
973 cn.parentId = this.id;
974 cn.onRender(this.el, null);
988 * @class Roo.bootstrap.Button
989 * @extends Roo.bootstrap.Component
990 * Bootstrap Button class
991 * @cfg {String} html The button content
992 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
993 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
994 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
995 * @cfg {String} size (lg|sm|xs)
996 * @cfg {String} tag (a|input|submit)
997 * @cfg {String} href empty or href
998 * @cfg {Boolean} disabled default false;
999 * @cfg {Boolean} isClose default false;
1000 * @cfg {String} glyphicon depricated - use fa
1001 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1002 * @cfg {String} badge text for badge
1003 * @cfg {String} theme (default|glow)
1004 * @cfg {Boolean} inverse dark themed version
1005 * @cfg {Boolean} toggle is it a slidy toggle button
1006 * @cfg {Boolean} pressed default null - if the button ahs active state
1007 * @cfg {String} ontext text for on slidy toggle state
1008 * @cfg {String} offtext text for off slidy toggle state
1009 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1010 * @cfg {Boolean} removeClass remove the standard class..
1011 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1012 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1015 * Create a new button
1016 * @param {Object} config The config object
1020 Roo.bootstrap.Button = function(config){
1021 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1027 * When a button is pressed
1028 * @param {Roo.bootstrap.Button} btn
1029 * @param {Roo.EventObject} e
1034 * When a button is double clicked
1035 * @param {Roo.bootstrap.Button} btn
1036 * @param {Roo.EventObject} e
1041 * After the button has been toggles
1042 * @param {Roo.bootstrap.Button} btn
1043 * @param {Roo.EventObject} e
1044 * @param {boolean} pressed (also available as button.pressed)
1050 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1071 preventDefault: true,
1080 getAutoCreate : function(){
1088 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1089 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1090 this.tag = 'button';
1094 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1096 if (this.toggle == true) {
1099 cls: 'slider-frame roo-button',
1103 'data-on-text':'ON',
1104 'data-off-text':'OFF',
1105 cls: 'slider-button',
1110 // why are we validating the weights?
1111 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1112 cfg.cls += ' ' + this.weight;
1119 cfg.cls += ' close';
1121 cfg["aria-hidden"] = true;
1123 cfg.html = "×";
1129 if (this.theme==='default') {
1130 cfg.cls = 'btn roo-button';
1132 //if (this.parentType != 'Navbar') {
1133 this.weight = this.weight.length ? this.weight : 'default';
1135 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1137 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1138 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1139 cfg.cls += ' btn-' + outline + weight;
1140 if (this.weight == 'default') {
1142 cfg.cls += ' btn-' + this.weight;
1145 } else if (this.theme==='glow') {
1148 cfg.cls = 'btn-glow roo-button';
1150 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1152 cfg.cls += ' ' + this.weight;
1158 this.cls += ' inverse';
1162 if (this.active || this.pressed === true) {
1163 cfg.cls += ' active';
1166 if (this.disabled) {
1167 cfg.disabled = 'disabled';
1171 Roo.log('changing to ul' );
1173 this.glyphicon = 'caret';
1174 if (Roo.bootstrap.version == 4) {
1175 this.fa = 'caret-down';
1180 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1182 //gsRoo.log(this.parentType);
1183 if (this.parentType === 'Navbar' && !this.parent().bar) {
1184 Roo.log('changing to li?');
1193 href : this.href || '#'
1196 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1197 cfg.cls += ' dropdown';
1204 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1206 if (this.glyphicon) {
1207 cfg.html = ' ' + cfg.html;
1212 cls: 'glyphicon glyphicon-' + this.glyphicon
1217 cfg.html = ' ' + cfg.html;
1222 cls: 'fa fas fa-' + this.fa
1232 // cfg.cls='btn roo-button';
1236 var value = cfg.html;
1241 cls: 'glyphicon glyphicon-' + this.glyphicon,
1248 cls: 'fa fas fa-' + this.fa,
1253 var bw = this.badge_weight.length ? this.badge_weight :
1254 (this.weight.length ? this.weight : 'secondary');
1255 bw = bw == 'default' ? 'secondary' : bw;
1261 cls: 'badge badge-' + bw,
1270 cfg.cls += ' dropdown';
1271 cfg.html = typeof(cfg.html) != 'undefined' ?
1272 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1275 if (cfg.tag !== 'a' && this.href !== '') {
1276 throw "Tag must be a to set href.";
1277 } else if (this.href.length > 0) {
1278 cfg.href = this.href;
1281 if(this.removeClass){
1286 cfg.target = this.target;
1291 initEvents: function() {
1292 // Roo.log('init events?');
1293 // Roo.log(this.el.dom);
1296 if (typeof (this.menu) != 'undefined') {
1297 this.menu.parentType = this.xtype;
1298 this.menu.triggerEl = this.el;
1299 this.addxtype(Roo.apply({}, this.menu));
1303 if (this.el.hasClass('roo-button')) {
1304 this.el.on('click', this.onClick, this);
1305 this.el.on('dblclick', this.onDblClick, this);
1307 this.el.select('.roo-button').on('click', this.onClick, this);
1308 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1312 if(this.removeClass){
1313 this.el.on('click', this.onClick, this);
1316 if (this.group === true) {
1317 if (this.pressed === false || this.pressed === true) {
1320 this.pressed = false;
1321 this.setActive(this.pressed);
1326 this.el.enableDisplayMode();
1329 onClick : function(e)
1331 if (this.disabled) {
1335 Roo.log('button on click ');
1336 if(this.preventDefault){
1345 this.setActive(true);
1346 var pi = this.parent().items;
1347 for (var i = 0;i < pi.length;i++) {
1348 if (this == pi[i]) {
1351 if (pi[i].el.hasClass('roo-button')) {
1352 pi[i].setActive(false);
1355 this.fireEvent('click', this, e);
1359 if (this.pressed === true || this.pressed === false) {
1360 this.toggleActive(e);
1364 this.fireEvent('click', this, e);
1366 onDblClick: function(e)
1368 if (this.disabled) {
1371 if(this.preventDefault){
1374 this.fireEvent('dblclick', this, e);
1377 * Enables this button
1381 this.disabled = false;
1382 this.el.removeClass('disabled');
1383 this.el.dom.removeAttribute("disabled");
1387 * Disable this button
1389 disable : function()
1391 this.disabled = true;
1392 this.el.addClass('disabled');
1393 this.el.attr("disabled", "disabled")
1396 * sets the active state on/off,
1397 * @param {Boolean} state (optional) Force a particular state
1399 setActive : function(v) {
1401 this.el[v ? 'addClass' : 'removeClass']('active');
1405 * toggles the current active state
1407 toggleActive : function(e)
1409 this.setActive(!this.pressed); // this modifies pressed...
1410 this.fireEvent('toggle', this, e, this.pressed);
1413 * get the current active state
1414 * @return {boolean} true if it's active
1416 isActive : function()
1418 return this.el.hasClass('active');
1421 * set the text of the first selected button
1423 setText : function(str)
1425 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1428 * get the text of the first selected button
1430 getText : function()
1432 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1435 setWeight : function(str)
1437 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1438 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1440 var outline = this.outline ? 'outline-' : '';
1441 if (str == 'default') {
1442 this.el.addClass('btn-default btn-outline-secondary');
1445 this.el.addClass('btn-' + outline + str);
1450 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1452 Roo.bootstrap.Button.weights = [
1472 * @class Roo.bootstrap.Column
1473 * @extends Roo.bootstrap.Component
1474 * @children Roo.bootstrap.Component
1475 * Bootstrap Column class
1476 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1477 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1478 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1479 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1480 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1481 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1482 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1483 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1486 * @cfg {Boolean} hidden (true|false) hide the element
1487 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1488 * @cfg {String} fa (ban|check|...) font awesome icon
1489 * @cfg {Number} fasize (1|2|....) font awsome size
1491 * @cfg {String} icon (info-sign|check|...) glyphicon name
1493 * @cfg {String} html content of column.
1496 * Create a new Column
1497 * @param {Object} config The config object
1500 Roo.bootstrap.Column = function(config){
1501 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1504 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1522 getAutoCreate : function(){
1523 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1531 var sizes = ['xs','sm','md','lg'];
1532 sizes.map(function(size ,ix){
1533 //Roo.log( size + ':' + settings[size]);
1535 if (settings[size+'off'] !== false) {
1536 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1539 if (settings[size] === false) {
1543 if (!settings[size]) { // 0 = hidden
1544 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1546 for (var i = ix; i > -1; i--) {
1547 cfg.cls += ' d-' + sizes[i] + '-none';
1553 cfg.cls += ' col-' + size + '-' + settings[size] + (
1554 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1560 cfg.cls += ' hidden';
1563 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1564 cfg.cls +=' alert alert-' + this.alert;
1568 if (this.html.length) {
1569 cfg.html = this.html;
1573 if (this.fasize > 1) {
1574 fasize = ' fa-' + this.fasize + 'x';
1576 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1581 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1600 * @class Roo.bootstrap.Container
1601 * @extends Roo.bootstrap.Component
1603 * @children Roo.bootstrap.Component
1604 * Bootstrap Container class
1605 * @cfg {Boolean} jumbotron is it a jumbotron element
1606 * @cfg {String} html content of element
1607 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1608 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1609 * @cfg {String} header content of header (for panel)
1610 * @cfg {String} footer content of footer (for panel)
1611 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1612 * @cfg {String} tag (header|aside|section) type of HTML tag.
1613 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1614 * @cfg {String} fa font awesome icon
1615 * @cfg {String} icon (info-sign|check|...) glyphicon name
1616 * @cfg {Boolean} hidden (true|false) hide the element
1617 * @cfg {Boolean} expandable (true|false) default false
1618 * @cfg {Boolean} expanded (true|false) default true
1619 * @cfg {String} rheader contet on the right of header
1620 * @cfg {Boolean} clickable (true|false) default false
1624 * Create a new Container
1625 * @param {Object} config The config object
1628 Roo.bootstrap.Container = function(config){
1629 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1635 * After the panel has been expand
1637 * @param {Roo.bootstrap.Container} this
1642 * After the panel has been collapsed
1644 * @param {Roo.bootstrap.Container} this
1649 * When a element is chick
1650 * @param {Roo.bootstrap.Container} this
1651 * @param {Roo.EventObject} e
1657 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1675 getChildContainer : function() {
1681 if (this.panel.length) {
1682 return this.el.select('.panel-body',true).first();
1689 getAutoCreate : function(){
1692 tag : this.tag || 'div',
1696 if (this.jumbotron) {
1697 cfg.cls = 'jumbotron';
1702 // - this is applied by the parent..
1704 // cfg.cls = this.cls + '';
1707 if (this.sticky.length) {
1709 var bd = Roo.get(document.body);
1710 if (!bd.hasClass('bootstrap-sticky')) {
1711 bd.addClass('bootstrap-sticky');
1712 Roo.select('html',true).setStyle('height', '100%');
1715 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1719 if (this.well.length) {
1720 switch (this.well) {
1723 cfg.cls +=' well well-' +this.well;
1732 cfg.cls += ' hidden';
1736 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1737 cfg.cls +=' alert alert-' + this.alert;
1742 if (this.panel.length) {
1743 cfg.cls += ' panel panel-' + this.panel;
1745 if (this.header.length) {
1749 if(this.expandable){
1751 cfg.cls = cfg.cls + ' expandable';
1755 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1763 cls : 'panel-title',
1764 html : (this.expandable ? ' ' : '') + this.header
1768 cls: 'panel-header-right',
1774 cls : 'panel-heading',
1775 style : this.expandable ? 'cursor: pointer' : '',
1783 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1788 if (this.footer.length) {
1790 cls : 'panel-footer',
1799 body.html = this.html || cfg.html;
1800 // prefix with the icons..
1802 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1805 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1810 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1811 cfg.cls = 'container';
1817 initEvents: function()
1819 if(this.expandable){
1820 var headerEl = this.headerEl();
1823 headerEl.on('click', this.onToggleClick, this);
1828 this.el.on('click', this.onClick, this);
1833 onToggleClick : function()
1835 var headerEl = this.headerEl();
1851 if(this.fireEvent('expand', this)) {
1853 this.expanded = true;
1855 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1857 this.el.select('.panel-body',true).first().removeClass('hide');
1859 var toggleEl = this.toggleEl();
1865 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1870 collapse : function()
1872 if(this.fireEvent('collapse', this)) {
1874 this.expanded = false;
1876 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1877 this.el.select('.panel-body',true).first().addClass('hide');
1879 var toggleEl = this.toggleEl();
1885 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1889 toggleEl : function()
1891 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1895 return this.el.select('.panel-heading .fa',true).first();
1898 headerEl : function()
1900 if(!this.el || !this.panel.length || !this.header.length){
1904 return this.el.select('.panel-heading',true).first()
1909 if(!this.el || !this.panel.length){
1913 return this.el.select('.panel-body',true).first()
1916 titleEl : function()
1918 if(!this.el || !this.panel.length || !this.header.length){
1922 return this.el.select('.panel-title',true).first();
1925 setTitle : function(v)
1927 var titleEl = this.titleEl();
1933 titleEl.dom.innerHTML = v;
1936 getTitle : function()
1939 var titleEl = this.titleEl();
1945 return titleEl.dom.innerHTML;
1948 setRightTitle : function(v)
1950 var t = this.el.select('.panel-header-right',true).first();
1956 t.dom.innerHTML = v;
1959 onClick : function(e)
1963 this.fireEvent('click', this, e);
1970 * This is BS4's Card element.. - similar to our containers probably..
1974 * @class Roo.bootstrap.Card
1975 * @extends Roo.bootstrap.Component
1976 * @children Roo.bootstrap.Component
1977 * Bootstrap Card class
1980 * possible... may not be implemented..
1981 * @cfg {String} header_image src url of image.
1982 * @cfg {String|Object} header
1983 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1984 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1986 * @cfg {String} title
1987 * @cfg {String} subtitle
1988 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1989 * @cfg {String} footer
1991 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1993 * @cfg {String} margin (0|1|2|3|4|5|auto)
1994 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1995 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1996 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1997 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1998 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1999 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2001 * @cfg {String} padding (0|1|2|3|4|5)
2002 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2003 * @cfg {String} padding_bottom (0|1|2|3|4|5)
2004 * @cfg {String} padding_left (0|1|2|3|4|5)
2005 * @cfg {String} padding_right (0|1|2|3|4|5)
2006 * @cfg {String} padding_x (0|1|2|3|4|5)
2007 * @cfg {String} padding_y (0|1|2|3|4|5)
2009 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2010 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2011 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015 * @config {Boolean} dragable if this card can be dragged.
2016 * @config {String} drag_group group for drag
2017 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2018 * @config {String} drop_group group for drag
2020 * @config {Boolean} collapsable can the body be collapsed.
2021 * @config {Boolean} collapsed is the body collapsed when rendered...
2022 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2023 * @config {Boolean} rotated is the body rotated when rendered...
2026 * Create a new Container
2027 * @param {Object} config The config object
2030 Roo.bootstrap.Card = function(config){
2031 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2037 * When a element a card is dropped
2038 * @param {Roo.bootstrap.Card} this
2041 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2042 * @param {String} position 'above' or 'below'
2043 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2049 * When a element a card is rotate
2050 * @param {Roo.bootstrap.Card} this
2051 * @param {Roo.Element} n the node being dropped?
2052 * @param {Boolean} rotate status
2057 * When a card element is dragged over ready to drop (return false to block dropable)
2058 * @param {Roo.bootstrap.Card} this
2059 * @param {Object} data from dragdrop
2067 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2072 margin: '', /// may be better in component?
2102 collapsable : false,
2111 childContainer : false,
2112 dropEl : false, /// the dom placeholde element that indicates drop location.
2113 containerEl: false, // body container
2114 bodyEl: false, // card-body
2115 headerContainerEl : false, //
2117 header_imageEl : false,
2120 layoutCls : function()
2124 Roo.log(this.margin_bottom.length);
2125 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2126 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2128 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2129 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2131 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2132 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2136 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2137 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2138 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2142 // more generic support?
2150 // Roo.log("Call onRender: " + this.xtype);
2151 /* We are looking at something like this.
2153 <img src="..." class="card-img-top" alt="...">
2154 <div class="card-body">
2155 <h5 class="card-title">Card title</h5>
2156 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2158 >> this bit is really the body...
2159 <div> << we will ad dthis in hopefully it will not break shit.
2161 ** card text does not actually have any styling...
2163 <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>
2166 <a href="#" class="card-link">Card link</a>
2169 <div class="card-footer">
2170 <small class="text-muted">Last updated 3 mins ago</small>
2174 getAutoCreate : function(){
2182 if (this.weight.length && this.weight != 'light') {
2183 cfg.cls += ' text-white';
2185 cfg.cls += ' text-dark'; // need as it's nested..
2187 if (this.weight.length) {
2188 cfg.cls += ' bg-' + this.weight;
2191 cfg.cls += ' ' + this.layoutCls();
2194 var hdr_ctr = false;
2195 if (this.header.length) {
2197 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2198 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2206 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2212 if (this.collapsable) {
2215 cls : 'd-block user-select-none',
2219 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2224 hdr.cn.push(hdr_ctr);
2229 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2234 if (this.header_image.length) {
2237 cls : 'card-img-top',
2238 src: this.header_image // escape?
2243 cls : 'card-img-top d-none'
2249 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2253 if (this.collapsable || this.rotateable) {
2256 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2263 if (this.title.length) {
2267 src: this.title // escape?
2271 if (this.subtitle.length) {
2275 src: this.subtitle // escape?
2281 cls : 'roo-card-body-ctr'
2284 if (this.html.length) {
2290 // fixme ? handle objects?
2292 if (this.footer.length) {
2295 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2300 cfg.cn.push({cls : 'card-footer d-none'});
2309 getCardHeader : function()
2311 var ret = this.el.select('.card-header',true).first();
2312 if (ret.hasClass('d-none')) {
2313 ret.removeClass('d-none');
2318 getCardFooter : function()
2320 var ret = this.el.select('.card-footer',true).first();
2321 if (ret.hasClass('d-none')) {
2322 ret.removeClass('d-none');
2327 getCardImageTop : function()
2329 var ret = this.header_imageEl;
2330 if (ret.hasClass('d-none')) {
2331 ret.removeClass('d-none');
2337 getChildContainer : function()
2343 return this.el.select('.roo-card-body-ctr',true).first();
2346 initEvents: function()
2348 this.bodyEl = this.el.select('.card-body',true).first();
2349 this.containerEl = this.getChildContainer();
2351 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2352 containerScroll: true,
2353 ddGroup: this.drag_group || 'default_card_drag_group'
2355 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2357 if (this.dropable) {
2358 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2359 containerScroll: true,
2360 ddGroup: this.drop_group || 'default_card_drag_group'
2362 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2363 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2364 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2365 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2366 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2369 if (this.collapsable) {
2370 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2372 if (this.rotateable) {
2373 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2375 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2377 this.footerEl = this.el.select('.card-footer',true).first();
2378 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2379 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2380 this.headerEl = this.el.select('.card-header',true).first();
2383 this.el.addClass('roo-card-rotated');
2384 this.fireEvent('rotate', this, true);
2386 this.header_imageEl = this.el.select('.card-img-top',true).first();
2387 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2390 getDragData : function(e)
2392 var target = this.getEl();
2394 //this.handleSelection(e);
2399 nodes: this.getEl(),
2404 dragData.ddel = target.dom ; // the div element
2405 Roo.log(target.getWidth( ));
2406 dragData.ddel.style.width = target.getWidth() + 'px';
2413 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2414 * whole Element becomes the target, and this causes the drop gesture to append.
2416 * Returns an object:
2419 position : 'below' or 'above'
2420 card : relateive to card OBJECT (or true for no cards listed)
2421 items_n : relative to nth item in list
2422 card_n : relative to nth card in list
2427 getTargetFromEvent : function(e, dragged_card_el)
2429 var target = e.getTarget();
2430 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2431 target = target.parentNode;
2442 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2443 // see if target is one of the 'cards'...
2446 //Roo.log(this.items.length);
2449 var last_card_n = 0;
2451 for (var i = 0;i< this.items.length;i++) {
2453 if (!this.items[i].el.hasClass('card')) {
2456 pos = this.getDropPoint(e, this.items[i].el.dom);
2458 cards_len = ret.cards.length;
2459 //Roo.log(this.items[i].el.dom.id);
2460 ret.cards.push(this.items[i]);
2462 if (ret.card_n < 0 && pos == 'above') {
2463 ret.position = cards_len > 0 ? 'below' : pos;
2464 ret.items_n = i > 0 ? i - 1 : 0;
2465 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2466 ret.card = ret.cards[ret.card_n];
2469 if (!ret.cards.length) {
2471 ret.position = 'below';
2475 // could not find a card.. stick it at the end..
2476 if (ret.card_n < 0) {
2477 ret.card_n = last_card_n;
2478 ret.card = ret.cards[last_card_n];
2479 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2480 ret.position = 'below';
2483 if (this.items[ret.items_n].el == dragged_card_el) {
2487 if (ret.position == 'below') {
2488 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2490 if (card_after && card_after.el == dragged_card_el) {
2497 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2499 if (card_before && card_before.el == dragged_card_el) {
2506 onNodeEnter : function(n, dd, e, data){
2509 onNodeOver : function(n, dd, e, data)
2512 var target_info = this.getTargetFromEvent(e,data.source.el);
2513 if (target_info === false) {
2514 this.dropPlaceHolder('hide');
2517 Roo.log(['getTargetFromEvent', target_info ]);
2520 if (this.fireEvent('cardover', this, [ data ]) === false) {
2524 this.dropPlaceHolder('show', target_info,data);
2528 onNodeOut : function(n, dd, e, data){
2529 this.dropPlaceHolder('hide');
2532 onNodeDrop : function(n, dd, e, data)
2535 // call drop - return false if
2537 // this could actually fail - if the Network drops..
2538 // we will ignore this at present..- client should probably reload
2539 // the whole set of cards if stuff like that fails.
2542 var info = this.getTargetFromEvent(e,data.source.el);
2543 if (info === false) {
2546 this.dropPlaceHolder('hide');
2550 this.acceptCard(data.source, info.position, info.card, info.items_n);
2554 firstChildCard : function()
2556 for (var i = 0;i< this.items.length;i++) {
2558 if (!this.items[i].el.hasClass('card')) {
2561 return this.items[i];
2563 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2568 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2570 acceptCard : function(move_card, position, next_to_card )
2572 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2576 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2578 move_card.parent().removeCard(move_card);
2581 var dom = move_card.el.dom;
2582 dom.style.width = ''; // clear with - which is set by drag.
2584 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2585 var cardel = next_to_card.el.dom;
2587 if (position == 'above' ) {
2588 cardel.parentNode.insertBefore(dom, cardel);
2589 } else if (cardel.nextSibling) {
2590 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2592 cardel.parentNode.append(dom);
2595 // card container???
2596 this.containerEl.dom.append(dom);
2599 //FIXME HANDLE card = true
2601 // add this to the correct place in items.
2603 // remove Card from items.
2606 if (this.items.length) {
2608 //Roo.log([info.items_n, info.position, this.items.length]);
2609 for (var i =0; i < this.items.length; i++) {
2610 if (i == to_items_n && position == 'above') {
2611 nitems.push(move_card);
2613 nitems.push(this.items[i]);
2614 if (i == to_items_n && position == 'below') {
2615 nitems.push(move_card);
2618 this.items = nitems;
2619 Roo.log(this.items);
2621 this.items.push(move_card);
2624 move_card.parentId = this.id;
2630 removeCard : function(c)
2632 this.items = this.items.filter(function(e) { return e != c });
2635 dom.parentNode.removeChild(dom);
2636 dom.style.width = ''; // clear with - which is set by drag.
2641 /** Decide whether to drop above or below a View node. */
2642 getDropPoint : function(e, n, dd)
2647 if (n == this.containerEl.dom) {
2650 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2651 var c = t + (b - t) / 2;
2652 var y = Roo.lib.Event.getPageY(e);
2659 onToggleCollapse : function(e)
2661 if (this.collapsed) {
2662 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2663 this.collapsableEl.addClass('show');
2664 this.collapsed = false;
2667 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2668 this.collapsableEl.removeClass('show');
2669 this.collapsed = true;
2674 onToggleRotate : function(e)
2676 this.collapsableEl.removeClass('show');
2677 this.footerEl.removeClass('d-none');
2678 this.el.removeClass('roo-card-rotated');
2679 this.el.removeClass('d-none');
2682 this.collapsableEl.addClass('show');
2683 this.rotated = false;
2684 this.fireEvent('rotate', this, this.rotated);
2687 this.el.addClass('roo-card-rotated');
2688 this.footerEl.addClass('d-none');
2689 this.el.select('.roo-collapsable').removeClass('show');
2691 this.rotated = true;
2692 this.fireEvent('rotate', this, this.rotated);
2696 dropPlaceHolder: function (action, info, data)
2698 if (this.dropEl === false) {
2699 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2703 this.dropEl.removeClass(['d-none', 'd-block']);
2704 if (action == 'hide') {
2706 this.dropEl.addClass('d-none');
2709 // FIXME - info.card == true!!!
2710 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2712 if (info.card !== true) {
2713 var cardel = info.card.el.dom;
2715 if (info.position == 'above') {
2716 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2717 } else if (cardel.nextSibling) {
2718 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2720 cardel.parentNode.append(this.dropEl.dom);
2723 // card container???
2724 this.containerEl.dom.append(this.dropEl.dom);
2727 this.dropEl.addClass('d-block roo-card-dropzone');
2729 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2736 setHeaderText: function(html)
2739 if (this.headerContainerEl) {
2740 this.headerContainerEl.dom.innerHTML = html;
2743 onHeaderImageLoad : function(ev, he)
2745 if (!this.header_image_fit_square) {
2749 var hw = he.naturalHeight / he.naturalWidth;
2752 //var w = he.dom.naturalWidth;
2755 he.style.position = 'relative';
2757 var nw = (ww * (1/hw));
2758 Roo.get(he).setSize( ww * (1/hw), ww);
2759 he.style.left = ((ww - nw)/ 2) + 'px';
2760 he.style.position = 'relative';
2771 * Card header - holder for the card header elements.
2776 * @class Roo.bootstrap.CardHeader
2777 * @extends Roo.bootstrap.Element
2778 * Bootstrap CardHeader class
2780 * Create a new Card Header - that you can embed children into
2781 * @param {Object} config The config object
2784 Roo.bootstrap.CardHeader = function(config){
2785 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2788 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2791 container_method : 'getCardHeader'
2804 * Card footer - holder for the card footer elements.
2809 * @class Roo.bootstrap.CardFooter
2810 * @extends Roo.bootstrap.Element
2811 * Bootstrap CardFooter class
2813 * Create a new Card Footer - that you can embed children into
2814 * @param {Object} config The config object
2817 Roo.bootstrap.CardFooter = function(config){
2818 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2821 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2824 container_method : 'getCardFooter'
2837 * Card header - holder for the card header elements.
2842 * @class Roo.bootstrap.CardImageTop
2843 * @extends Roo.bootstrap.Element
2844 * Bootstrap CardImageTop class
2846 * Create a new Card Image Top container
2847 * @param {Object} config The config object
2850 Roo.bootstrap.CardImageTop = function(config){
2851 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2854 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2857 container_method : 'getCardImageTop'
2872 * @class Roo.bootstrap.ButtonUploader
2873 * @extends Roo.bootstrap.Button
2874 * Bootstrap Button Uploader class - it's a button which when you add files to it
2877 * @cfg {Number} errorTimeout default 3000
2878 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2879 * @cfg {Array} html The button text.
2880 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2883 * Create a new CardUploader
2884 * @param {Object} config The config object
2887 Roo.bootstrap.ButtonUploader = function(config){
2891 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2897 * @event beforeselect
2898 * When button is pressed, before show upload files dialog is shown
2899 * @param {Roo.bootstrap.UploaderButton} this
2902 'beforeselect' : true,
2904 * @event fired when files have been selected,
2905 * When a the download link is clicked
2906 * @param {Roo.bootstrap.UploaderButton} this
2907 * @param {Array} Array of files that have been uploaded
2914 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2917 errorTimeout : 3000,
2921 fileCollection : false,
2926 getAutoCreate : function()
2931 cls : 'd-none roo-card-upload-selector'
2934 if (this.multiple) {
2935 im.multiple = 'multiple';
2941 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2951 initEvents : function()
2954 Roo.bootstrap.Button.prototype.initEvents.call(this);
2960 this.urlAPI = (window.createObjectURL && window) ||
2961 (window.URL && URL.revokeObjectURL && URL) ||
2962 (window.webkitURL && webkitURL);
2967 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2969 this.selectorEl.on('change', this.onFileSelected, this);
2976 onClick : function(e)
2980 if ( this.fireEvent('beforeselect', this) === false) {
2984 this.selectorEl.dom.click();
2988 onFileSelected : function(e)
2992 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2995 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2996 this.selectorEl.dom.value = '';// hopefully reset..
2998 this.fireEvent('uploaded', this, files );
3006 * addCard - add an Attachment to the uploader
3007 * @param data - the data about the image to upload
3011 title : "Title of file",
3012 is_uploaded : false,
3013 src : "http://.....",
3014 srcfile : { the File upload object },
3015 mimetype : file.type,
3018 .. any other data...
3043 * @class Roo.bootstrap.Img
3044 * @extends Roo.bootstrap.Component
3045 * Bootstrap Img class
3046 * @cfg {Boolean} imgResponsive false | true
3047 * @cfg {String} border rounded | circle | thumbnail
3048 * @cfg {String} src image source
3049 * @cfg {String} alt image alternative text
3050 * @cfg {String} href a tag href
3051 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3052 * @cfg {String} xsUrl xs image source
3053 * @cfg {String} smUrl sm image source
3054 * @cfg {String} mdUrl md image source
3055 * @cfg {String} lgUrl lg image source
3056 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3059 * Create a new Input
3060 * @param {Object} config The config object
3063 Roo.bootstrap.Img = function(config){
3064 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3070 * The img click event for the img.
3071 * @param {Roo.EventObject} e
3076 * The when any image loads
3077 * @param {Roo.EventObject} e
3083 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3085 imgResponsive: true,
3094 backgroundContain : false,
3096 getAutoCreate : function()
3098 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3099 return this.createSingleImg();
3104 cls: 'roo-image-responsive-group',
3109 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3111 if(!_this[size + 'Url']){
3117 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3118 html: _this.html || cfg.html,
3119 src: _this[size + 'Url']
3122 img.cls += ' roo-image-responsive-' + size;
3124 var s = ['xs', 'sm', 'md', 'lg'];
3126 s.splice(s.indexOf(size), 1);
3128 Roo.each(s, function(ss){
3129 img.cls += ' hidden-' + ss;
3132 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3133 cfg.cls += ' img-' + _this.border;
3137 cfg.alt = _this.alt;
3150 a.target = _this.target;
3154 cfg.cn.push((_this.href) ? a : img);
3161 createSingleImg : function()
3165 cls: (this.imgResponsive) ? 'img-responsive' : '',
3167 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3170 if (this.backgroundContain) {
3171 cfg.cls += ' background-contain';
3174 cfg.html = this.html || cfg.html;
3176 if (this.backgroundContain) {
3177 cfg.style="background-image: url(" + this.src + ')';
3179 cfg.src = this.src || cfg.src;
3182 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3183 cfg.cls += ' img-' + this.border;
3200 a.target = this.target;
3205 return (this.href) ? a : cfg;
3208 initEvents: function()
3211 this.el.on('click', this.onClick, this);
3213 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3214 this.el.on('load', this.onImageLoad, this);
3216 // not sure if this works.. not tested
3217 this.el.select('img', true).on('load', this.onImageLoad, this);
3222 onClick : function(e)
3224 Roo.log('img onclick');
3225 this.fireEvent('click', this, e);
3227 onImageLoad: function(e)
3229 Roo.log('img load');
3230 this.fireEvent('load', this, e);
3234 * Sets the url of the image - used to update it
3235 * @param {String} url the url of the image
3238 setSrc : function(url)
3242 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3243 if (this.backgroundContain) {
3244 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3246 this.el.dom.src = url;
3251 this.el.select('img', true).first().dom.src = url;
3267 * @class Roo.bootstrap.Link
3268 * @extends Roo.bootstrap.Component
3269 * Bootstrap Link Class
3270 * @cfg {String} alt image alternative text
3271 * @cfg {String} href a tag href
3272 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3273 * @cfg {String} html the content of the link.
3274 * @cfg {String} anchor name for the anchor link
3275 * @cfg {String} fa - favicon
3277 * @cfg {Boolean} preventDefault (true | false) default false
3281 * Create a new Input
3282 * @param {Object} config The config object
3285 Roo.bootstrap.Link = function(config){
3286 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3292 * The img click event for the img.
3293 * @param {Roo.EventObject} e
3299 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3303 preventDefault: false,
3309 getAutoCreate : function()
3311 var html = this.html || '';
3313 if (this.fa !== false) {
3314 html = '<i class="fa fa-' + this.fa + '"></i>';
3319 // anchor's do not require html/href...
3320 if (this.anchor === false) {
3322 cfg.href = this.href || '#';
3324 cfg.name = this.anchor;
3325 if (this.html !== false || this.fa !== false) {
3328 if (this.href !== false) {
3329 cfg.href = this.href;
3333 if(this.alt !== false){
3338 if(this.target !== false) {
3339 cfg.target = this.target;
3345 initEvents: function() {
3347 if(!this.href || this.preventDefault){
3348 this.el.on('click', this.onClick, this);
3352 onClick : function(e)
3354 if(this.preventDefault){
3357 //Roo.log('img onclick');
3358 this.fireEvent('click', this, e);
3371 * @class Roo.bootstrap.Header
3372 * @extends Roo.bootstrap.Component
3373 * Bootstrap Header class
3374 * @cfg {String} html content of header
3375 * @cfg {Number} level (1|2|3|4|5|6) default 1
3378 * Create a new Header
3379 * @param {Object} config The config object
3383 Roo.bootstrap.Header = function(config){
3384 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3387 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3395 getAutoCreate : function(){
3400 tag: 'h' + (1 *this.level),
3401 html: this.html || ''
3413 * Ext JS Library 1.1.1
3414 * Copyright(c) 2006-2007, Ext JS, LLC.
3416 * Originally Released Under LGPL - original licence link has changed is not relivant.
3419 * <script type="text/javascript">
3423 * @class Roo.bootstrap.MenuMgr
3424 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3427 Roo.bootstrap.MenuMgr = function(){
3428 var menus, active, groups = {}, attached = false, lastShow = new Date();
3430 // private - called when first menu is created
3433 active = new Roo.util.MixedCollection();
3434 Roo.get(document).addKeyListener(27, function(){
3435 if(active.length > 0){
3443 if(active && active.length > 0){
3444 var c = active.clone();
3454 if(active.length < 1){
3455 Roo.get(document).un("mouseup", onMouseDown);
3463 var last = active.last();
3464 lastShow = new Date();
3467 Roo.get(document).on("mouseup", onMouseDown);
3472 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3473 m.parentMenu.activeChild = m;
3474 }else if(last && last.isVisible()){
3475 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3480 function onBeforeHide(m){
3482 m.activeChild.hide();
3484 if(m.autoHideTimer){
3485 clearTimeout(m.autoHideTimer);
3486 delete m.autoHideTimer;
3491 function onBeforeShow(m){
3492 var pm = m.parentMenu;
3493 if(!pm && !m.allowOtherMenus){
3495 }else if(pm && pm.activeChild && active != m){
3496 pm.activeChild.hide();
3500 // private this should really trigger on mouseup..
3501 function onMouseDown(e){
3502 Roo.log("on Mouse Up");
3504 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3505 Roo.log("MenuManager hideAll");
3514 function onBeforeCheck(mi, state){
3516 var g = groups[mi.group];
3517 for(var i = 0, l = g.length; i < l; i++){
3519 g[i].setChecked(false);
3528 * Hides all menus that are currently visible
3530 hideAll : function(){
3535 register : function(menu){
3539 menus[menu.id] = menu;
3540 menu.on("beforehide", onBeforeHide);
3541 menu.on("hide", onHide);
3542 menu.on("beforeshow", onBeforeShow);
3543 menu.on("show", onShow);
3545 if(g && menu.events["checkchange"]){
3549 groups[g].push(menu);
3550 menu.on("checkchange", onCheck);
3555 * Returns a {@link Roo.menu.Menu} object
3556 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3557 * be used to generate and return a new Menu instance.
3559 get : function(menu){
3560 if(typeof menu == "string"){ // menu id
3562 }else if(menu.events){ // menu instance
3565 /*else if(typeof menu.length == 'number'){ // array of menu items?
3566 return new Roo.bootstrap.Menu({items:menu});
3567 }else{ // otherwise, must be a config
3568 return new Roo.bootstrap.Menu(menu);
3575 unregister : function(menu){
3576 delete menus[menu.id];
3577 menu.un("beforehide", onBeforeHide);
3578 menu.un("hide", onHide);
3579 menu.un("beforeshow", onBeforeShow);
3580 menu.un("show", onShow);
3582 if(g && menu.events["checkchange"]){
3583 groups[g].remove(menu);
3584 menu.un("checkchange", onCheck);
3589 registerCheckable : function(menuItem){
3590 var g = menuItem.group;
3595 groups[g].push(menuItem);
3596 menuItem.on("beforecheckchange", onBeforeCheck);
3601 unregisterCheckable : function(menuItem){
3602 var g = menuItem.group;
3604 groups[g].remove(menuItem);
3605 menuItem.un("beforecheckchange", onBeforeCheck);
3617 * @class Roo.bootstrap.Menu
3618 * @extends Roo.bootstrap.Component
3619 * Bootstrap Menu class - container for MenuItems
3620 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3621 * @cfg {bool} hidden if the menu should be hidden when rendered.
3622 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3623 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3624 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3625 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3629 * @param {Object} config The config object
3633 Roo.bootstrap.Menu = function(config){
3635 if (config.type == 'treeview') {
3636 // normally menu's are drawn attached to the document to handle layering etc..
3637 // however treeview (used by the docs menu is drawn into the parent element)
3638 this.container_method = 'getChildContainer';
3641 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3642 if (this.registerMenu && this.type != 'treeview') {
3643 Roo.bootstrap.MenuMgr.register(this);
3650 * Fires before this menu is displayed (return false to block)
3651 * @param {Roo.menu.Menu} this
3656 * Fires before this menu is hidden (return false to block)
3657 * @param {Roo.menu.Menu} this
3662 * Fires after this menu is displayed
3663 * @param {Roo.menu.Menu} this
3668 * Fires after this menu is hidden
3669 * @param {Roo.menu.Menu} this
3674 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3675 * @param {Roo.menu.Menu} this
3676 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3677 * @param {Roo.EventObject} e
3682 * Fires when the mouse is hovering over this menu
3683 * @param {Roo.menu.Menu} this
3684 * @param {Roo.EventObject} e
3685 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3690 * Fires when the mouse exits this menu
3691 * @param {Roo.menu.Menu} this
3692 * @param {Roo.EventObject} e
3693 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3698 * Fires when a menu item contained in this menu is clicked
3699 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3700 * @param {Roo.EventObject} e
3704 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3707 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3711 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3714 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3716 registerMenu : true,
3718 menuItems :false, // stores the menu items..
3728 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3730 hideTrigger : false,
3735 getChildContainer : function() {
3739 getAutoCreate : function(){
3741 //if (['right'].indexOf(this.align)!==-1) {
3742 // cfg.cn[1].cls += ' pull-right'
3747 cls : 'dropdown-menu shadow' ,
3748 style : 'z-index:1000'
3752 if (this.type === 'submenu') {
3753 cfg.cls = 'submenu active';
3755 if (this.type === 'treeview') {
3756 cfg.cls = 'treeview-menu';
3761 initEvents : function() {
3763 // Roo.log("ADD event");
3764 // Roo.log(this.triggerEl.dom);
3765 if (this.triggerEl) {
3767 this.triggerEl.on('click', this.onTriggerClick, this);
3769 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3771 if (!this.hideTrigger) {
3772 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3773 // dropdown toggle on the 'a' in BS4?
3774 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3776 this.triggerEl.addClass('dropdown-toggle');
3782 this.el.on('touchstart' , this.onTouch, this);
3784 this.el.on('click' , this.onClick, this);
3786 this.el.on("mouseover", this.onMouseOver, this);
3787 this.el.on("mouseout", this.onMouseOut, this);
3791 findTargetItem : function(e)
3793 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3797 //Roo.log(t); Roo.log(t.id);
3799 //Roo.log(this.menuitems);
3800 return this.menuitems.get(t.id);
3802 //return this.items.get(t.menuItemId);
3808 onTouch : function(e)
3810 Roo.log("menu.onTouch");
3811 //e.stopEvent(); this make the user popdown broken
3815 onClick : function(e)
3817 Roo.log("menu.onClick");
3819 var t = this.findTargetItem(e);
3820 if(!t || t.isContainer){
3825 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3826 if(t == this.activeItem && t.shouldDeactivate(e)){
3827 this.activeItem.deactivate();
3828 delete this.activeItem;
3832 this.setActiveItem(t, true);
3840 Roo.log('pass click event');
3844 this.fireEvent("click", this, t, e);
3848 if(!t.href.length || t.href == '#'){
3849 (function() { _this.hide(); }).defer(100);
3854 onMouseOver : function(e){
3855 var t = this.findTargetItem(e);
3858 // if(t.canActivate && !t.disabled){
3859 // this.setActiveItem(t, true);
3863 this.fireEvent("mouseover", this, e, t);
3865 isVisible : function(){
3866 return !this.hidden;
3868 onMouseOut : function(e){
3869 var t = this.findTargetItem(e);
3872 // if(t == this.activeItem && t.shouldDeactivate(e)){
3873 // this.activeItem.deactivate();
3874 // delete this.activeItem;
3877 this.fireEvent("mouseout", this, e, t);
3882 * Displays this menu relative to another element
3883 * @param {String/HTMLElement/Roo.Element} element The element to align to
3884 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3885 * the element (defaults to this.defaultAlign)
3886 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3888 show : function(el, pos, parentMenu)
3890 if (false === this.fireEvent("beforeshow", this)) {
3891 Roo.log("show canceled");
3894 this.parentMenu = parentMenu;
3898 this.el.addClass('show'); // show otherwise we do not know how big we are..
3900 var xy = this.el.getAlignToXY(el, pos);
3902 // bl-tl << left align below
3903 // tl-bl << left align
3905 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3906 // if it goes to far to the right.. -> align left.
3907 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3910 // was left align - go right?
3911 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3914 // goes down the bottom
3915 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3917 var a = this.align.replace('?', '').split('-');
3918 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3922 this.showAt( xy , parentMenu, false);
3925 * Displays this menu at a specific xy position
3926 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3927 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3929 showAt : function(xy, parentMenu, /* private: */_e){
3930 this.parentMenu = parentMenu;
3935 this.fireEvent("beforeshow", this);
3936 //xy = this.el.adjustForConstraints(xy);
3940 this.hideMenuItems();
3941 this.hidden = false;
3942 if (this.triggerEl) {
3943 this.triggerEl.addClass('open');
3946 this.el.addClass('show');
3950 // reassign x when hitting right
3952 // reassign y when hitting bottom
3954 // but the list may align on trigger left or trigger top... should it be a properity?
3956 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3961 this.fireEvent("show", this);
3967 this.doFocus.defer(50, this);
3971 doFocus : function(){
3973 this.focusEl.focus();
3978 * Hides this menu and optionally all parent menus
3979 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3981 hide : function(deep)
3983 if (false === this.fireEvent("beforehide", this)) {
3984 Roo.log("hide canceled");
3987 this.hideMenuItems();
3988 if(this.el && this.isVisible()){
3990 if(this.activeItem){
3991 this.activeItem.deactivate();
3992 this.activeItem = null;
3994 if (this.triggerEl) {
3995 this.triggerEl.removeClass('open');
3998 this.el.removeClass('show');
4000 this.fireEvent("hide", this);
4002 if(deep === true && this.parentMenu){
4003 this.parentMenu.hide(true);
4007 onTriggerClick : function(e)
4009 Roo.log('trigger click');
4011 var target = e.getTarget();
4013 Roo.log(target.nodeName.toLowerCase());
4015 if(target.nodeName.toLowerCase() === 'i'){
4021 onTriggerPress : function(e)
4023 Roo.log('trigger press');
4024 //Roo.log(e.getTarget());
4025 // Roo.log(this.triggerEl.dom);
4027 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4028 var pel = Roo.get(e.getTarget());
4029 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4030 Roo.log('is treeview or dropdown?');
4034 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4038 if (this.isVisible()) {
4044 this.show(this.triggerEl, this.align, false);
4047 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4054 hideMenuItems : function()
4056 Roo.log("hide Menu Items");
4061 this.el.select('.open',true).each(function(aa) {
4063 aa.removeClass('open');
4067 addxtypeChild : function (tree, cntr) {
4068 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4070 this.menuitems.add(comp);
4082 this.getEl().dom.innerHTML = '';
4083 this.menuitems.clear();
4097 * @class Roo.bootstrap.MenuItem
4098 * @extends Roo.bootstrap.Component
4099 * Bootstrap MenuItem class
4100 * @cfg {String} html the menu label
4101 * @cfg {String} href the link
4102 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4103 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4104 * @cfg {Boolean} active used on sidebars to highlight active itesm
4105 * @cfg {String} fa favicon to show on left of menu item.
4106 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4110 * Create a new MenuItem
4111 * @param {Object} config The config object
4115 Roo.bootstrap.MenuItem = function(config){
4116 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4121 * The raw click event for the entire grid.
4122 * @param {Roo.bootstrap.MenuItem} this
4123 * @param {Roo.EventObject} e
4129 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4133 preventDefault: false,
4134 isContainer : false,
4138 getAutoCreate : function(){
4140 if(this.isContainer){
4143 cls: 'dropdown-menu-item '
4153 cls : 'dropdown-item',
4158 if (this.fa !== false) {
4161 cls : 'fa fa-' + this.fa
4170 cls: 'dropdown-menu-item',
4173 if (this.parent().type == 'treeview') {
4174 cfg.cls = 'treeview-menu';
4177 cfg.cls += ' active';
4182 anc.href = this.href || cfg.cn[0].href ;
4183 ctag.html = this.html || cfg.cn[0].html ;
4187 initEvents: function()
4189 if (this.parent().type == 'treeview') {
4190 this.el.select('a').on('click', this.onClick, this);
4194 this.menu.parentType = this.xtype;
4195 this.menu.triggerEl = this.el;
4196 this.menu = this.addxtype(Roo.apply({}, this.menu));
4200 onClick : function(e)
4202 Roo.log('item on click ');
4204 if(this.preventDefault){
4207 //this.parent().hideMenuItems();
4209 this.fireEvent('click', this, e);
4228 * @class Roo.bootstrap.MenuSeparator
4229 * @extends Roo.bootstrap.Component
4230 * Bootstrap MenuSeparator class
4233 * Create a new MenuItem
4234 * @param {Object} config The config object
4238 Roo.bootstrap.MenuSeparator = function(config){
4239 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4242 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4244 getAutoCreate : function(){
4263 * @class Roo.bootstrap.Modal
4264 * @extends Roo.bootstrap.Component
4267 * @children Roo.bootstrap.Component
4268 * Bootstrap Modal class
4269 * @cfg {String} title Title of dialog
4270 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4271 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4272 * @cfg {Boolean} specificTitle default false
4273 * @cfg {Array} buttons Array of buttons or standard button set..
4274 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4275 * @cfg {Boolean} animate default true
4276 * @cfg {Boolean} allow_close default true
4277 * @cfg {Boolean} fitwindow default false
4278 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4279 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4280 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4281 * @cfg {String} size (sm|lg|xl) default empty
4282 * @cfg {Number} max_width set the max width of modal
4283 * @cfg {Boolean} editableTitle can the title be edited
4288 * Create a new Modal Dialog
4289 * @param {Object} config The config object
4292 Roo.bootstrap.Modal = function(config){
4293 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4298 * The raw btnclick event for the button
4299 * @param {Roo.EventObject} e
4304 * Fire when dialog resize
4305 * @param {Roo.bootstrap.Modal} this
4306 * @param {Roo.EventObject} e
4310 * @event titlechanged
4311 * Fire when the editable title has been changed
4312 * @param {Roo.bootstrap.Modal} this
4313 * @param {Roo.EventObject} value
4315 "titlechanged" : true
4318 this.buttons = this.buttons || [];
4321 this.tmpl = Roo.factory(this.tmpl);
4326 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4328 title : 'test dialog',
4338 specificTitle: false,
4340 buttonPosition: 'right',
4362 editableTitle : false,
4364 onRender : function(ct, position)
4366 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4369 var cfg = Roo.apply({}, this.getAutoCreate());
4372 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4374 //if (!cfg.name.length) {
4378 cfg.cls += ' ' + this.cls;
4381 cfg.style = this.style;
4383 this.el = Roo.get(document.body).createChild(cfg, position);
4385 //var type = this.el.dom.type;
4388 if(this.tabIndex !== undefined){
4389 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4392 this.dialogEl = this.el.select('.modal-dialog',true).first();
4393 this.bodyEl = this.el.select('.modal-body',true).first();
4394 this.closeEl = this.el.select('.modal-header .close', true).first();
4395 this.headerEl = this.el.select('.modal-header',true).first();
4396 this.titleEl = this.el.select('.modal-title',true).first();
4397 this.footerEl = this.el.select('.modal-footer',true).first();
4399 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4401 //this.el.addClass("x-dlg-modal");
4403 if (this.buttons.length) {
4404 Roo.each(this.buttons, function(bb) {
4405 var b = Roo.apply({}, bb);
4406 b.xns = b.xns || Roo.bootstrap;
4407 b.xtype = b.xtype || 'Button';
4408 if (typeof(b.listeners) == 'undefined') {
4409 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4412 var btn = Roo.factory(b);
4414 btn.render(this.getButtonContainer());
4418 // render the children.
4421 if(typeof(this.items) != 'undefined'){
4422 var items = this.items;
4425 for(var i =0;i < items.length;i++) {
4426 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4430 this.items = nitems;
4432 // where are these used - they used to be body/close/footer
4436 //this.el.addClass([this.fieldClass, this.cls]);
4440 getAutoCreate : function()
4442 // we will default to modal-body-overflow - might need to remove or make optional later.
4444 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4445 html : this.html || ''
4450 cls : 'modal-title',
4454 if(this.specificTitle){ // WTF is this?
4459 if (this.allow_close && Roo.bootstrap.version == 3) {
4469 if (this.editableTitle) {
4471 cls: 'form-control roo-editable-title d-none',
4477 if (this.allow_close && Roo.bootstrap.version == 4) {
4487 if(this.size.length){
4488 size = 'modal-' + this.size;
4491 var footer = Roo.bootstrap.version == 3 ?
4493 cls : 'modal-footer',
4497 cls: 'btn-' + this.buttonPosition
4502 { // BS4 uses mr-auto on left buttons....
4503 cls : 'modal-footer'
4514 cls: "modal-dialog " + size,
4517 cls : "modal-content",
4520 cls : 'modal-header',
4535 modal.cls += ' fade';
4541 getChildContainer : function() {
4546 getButtonContainer : function() {
4548 return Roo.bootstrap.version == 4 ?
4549 this.el.select('.modal-footer',true).first()
4550 : this.el.select('.modal-footer div',true).first();
4553 initEvents : function()
4555 if (this.allow_close) {
4556 this.closeEl.on('click', this.hide, this);
4558 Roo.EventManager.onWindowResize(this.resize, this, true);
4559 if (this.editableTitle) {
4560 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4561 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4562 this.headerEditEl.on('keyup', function(e) {
4563 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4564 this.toggleHeaderInput(false)
4567 this.headerEditEl.on('blur', function(e) {
4568 this.toggleHeaderInput(false)
4577 this.maskEl.setSize(
4578 Roo.lib.Dom.getViewWidth(true),
4579 Roo.lib.Dom.getViewHeight(true)
4582 if (this.fitwindow) {
4584 this.dialogEl.setStyle( { 'max-width' : '100%' });
4586 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4587 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4592 if(this.max_width !== 0) {
4594 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4597 this.setSize(w, this.height);
4601 if(this.max_height) {
4602 this.setSize(w,Math.min(
4604 Roo.lib.Dom.getViewportHeight(true) - 60
4610 if(!this.fit_content) {
4611 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4615 this.setSize(w, Math.min(
4617 this.headerEl.getHeight() +
4618 this.footerEl.getHeight() +
4619 this.getChildHeight(this.bodyEl.dom.childNodes),
4620 Roo.lib.Dom.getViewportHeight(true) - 60)
4626 setSize : function(w,h)
4637 if (!this.rendered) {
4640 this.toggleHeaderInput(false);
4641 //this.el.setStyle('display', 'block');
4642 this.el.removeClass('hideing');
4643 this.el.dom.style.display='block';
4645 Roo.get(document.body).addClass('modal-open');
4647 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4650 this.el.addClass('show');
4651 this.el.addClass('in');
4654 this.el.addClass('show');
4655 this.el.addClass('in');
4658 // not sure how we can show data in here..
4660 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4663 Roo.get(document.body).addClass("x-body-masked");
4665 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4666 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4667 this.maskEl.dom.style.display = 'block';
4668 this.maskEl.addClass('show');
4673 this.fireEvent('show', this);
4675 // set zindex here - otherwise it appears to be ignored...
4676 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4679 this.items.forEach( function(e) {
4680 e.layout ? e.layout() : false;
4688 if(this.fireEvent("beforehide", this) !== false){
4690 this.maskEl.removeClass('show');
4692 this.maskEl.dom.style.display = '';
4693 Roo.get(document.body).removeClass("x-body-masked");
4694 this.el.removeClass('in');
4695 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4697 if(this.animate){ // why
4698 this.el.addClass('hideing');
4699 this.el.removeClass('show');
4701 if (!this.el.hasClass('hideing')) {
4702 return; // it's been shown again...
4705 this.el.dom.style.display='';
4707 Roo.get(document.body).removeClass('modal-open');
4708 this.el.removeClass('hideing');
4712 this.el.removeClass('show');
4713 this.el.dom.style.display='';
4714 Roo.get(document.body).removeClass('modal-open');
4717 this.fireEvent('hide', this);
4720 isVisible : function()
4723 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4727 addButton : function(str, cb)
4731 var b = Roo.apply({}, { html : str } );
4732 b.xns = b.xns || Roo.bootstrap;
4733 b.xtype = b.xtype || 'Button';
4734 if (typeof(b.listeners) == 'undefined') {
4735 b.listeners = { click : cb.createDelegate(this) };
4738 var btn = Roo.factory(b);
4740 btn.render(this.getButtonContainer());
4746 setDefaultButton : function(btn)
4748 //this.el.select('.modal-footer').()
4751 resizeTo: function(w,h)
4753 this.dialogEl.setWidth(w);
4755 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4757 this.bodyEl.setHeight(h - diff);
4759 this.fireEvent('resize', this);
4762 setContentSize : function(w, h)
4766 onButtonClick: function(btn,e)
4769 this.fireEvent('btnclick', btn.name, e);
4772 * Set the title of the Dialog
4773 * @param {String} str new Title
4775 setTitle: function(str) {
4776 this.titleEl.dom.innerHTML = str;
4780 * Set the body of the Dialog
4781 * @param {String} str new Title
4783 setBody: function(str) {
4784 this.bodyEl.dom.innerHTML = str;
4787 * Set the body of the Dialog using the template
4788 * @param {Obj} data - apply this data to the template and replace the body contents.
4790 applyBody: function(obj)
4793 Roo.log("Error - using apply Body without a template");
4796 this.tmpl.overwrite(this.bodyEl, obj);
4799 getChildHeight : function(child_nodes)
4803 child_nodes.length == 0
4808 var child_height = 0;
4810 for(var i = 0; i < child_nodes.length; i++) {
4813 * for modal with tabs...
4814 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4816 var layout_childs = child_nodes[i].childNodes;
4818 for(var j = 0; j < layout_childs.length; j++) {
4820 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4822 var layout_body_childs = layout_childs[j].childNodes;
4824 for(var k = 0; k < layout_body_childs.length; k++) {
4826 if(layout_body_childs[k].classList.contains('navbar')) {
4827 child_height += layout_body_childs[k].offsetHeight;
4831 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4833 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4835 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4837 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4838 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4853 child_height += child_nodes[i].offsetHeight;
4854 // Roo.log(child_nodes[i].offsetHeight);
4857 return child_height;
4859 toggleHeaderInput : function(is_edit)
4861 if (!this.editableTitle) {
4862 return; // not editable.
4864 if (is_edit && this.is_header_editing) {
4865 return; // already editing..
4869 this.headerEditEl.dom.value = this.title;
4870 this.headerEditEl.removeClass('d-none');
4871 this.headerEditEl.dom.focus();
4872 this.titleEl.addClass('d-none');
4874 this.is_header_editing = true;
4877 // flip back to not editing.
4878 this.title = this.headerEditEl.dom.value;
4879 this.headerEditEl.addClass('d-none');
4880 this.titleEl.removeClass('d-none');
4881 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4882 this.is_header_editing = false;
4883 this.fireEvent('titlechanged', this, this.title);
4892 Roo.apply(Roo.bootstrap.Modal, {
4894 * Button config that displays a single OK button
4903 * Button config that displays Yes and No buttons
4919 * Button config that displays OK and Cancel buttons
4934 * Button config that displays Yes, No and Cancel buttons
4959 * messagebox - can be used as a replace
4963 * @class Roo.MessageBox
4964 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4968 Roo.Msg.alert('Status', 'Changes saved successfully.');
4970 // Prompt for user data:
4971 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4973 // process text value...
4977 // Show a dialog using config options:
4979 title:'Save Changes?',
4980 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4981 buttons: Roo.Msg.YESNOCANCEL,
4988 Roo.bootstrap.MessageBox = function(){
4989 var dlg, opt, mask, waitTimer;
4990 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4991 var buttons, activeTextEl, bwidth;
4995 var handleButton = function(button){
4997 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5001 var handleHide = function(){
5003 dlg.el.removeClass(opt.cls);
5006 // Roo.TaskMgr.stop(waitTimer);
5007 // waitTimer = null;
5012 var updateButtons = function(b){
5015 buttons["ok"].hide();
5016 buttons["cancel"].hide();
5017 buttons["yes"].hide();
5018 buttons["no"].hide();
5019 dlg.footerEl.hide();
5023 dlg.footerEl.show();
5024 for(var k in buttons){
5025 if(typeof buttons[k] != "function"){
5028 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5029 width += buttons[k].el.getWidth()+15;
5039 var handleEsc = function(d, k, e){
5040 if(opt && opt.closable !== false){
5050 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5051 * @return {Roo.BasicDialog} The BasicDialog element
5053 getDialog : function(){
5055 dlg = new Roo.bootstrap.Modal( {
5058 //constraintoviewport:false,
5060 //collapsible : false,
5065 //buttonAlign:"center",
5066 closeClick : function(){
5067 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5070 handleButton("cancel");
5075 dlg.on("hide", handleHide);
5077 //dlg.addKeyListener(27, handleEsc);
5079 this.buttons = buttons;
5080 var bt = this.buttonText;
5081 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5082 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5083 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5084 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5086 bodyEl = dlg.bodyEl.createChild({
5088 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5089 '<textarea class="roo-mb-textarea"></textarea>' +
5090 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5092 msgEl = bodyEl.dom.firstChild;
5093 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5094 textboxEl.enableDisplayMode();
5095 textboxEl.addKeyListener([10,13], function(){
5096 if(dlg.isVisible() && opt && opt.buttons){
5099 }else if(opt.buttons.yes){
5100 handleButton("yes");
5104 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5105 textareaEl.enableDisplayMode();
5106 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5107 progressEl.enableDisplayMode();
5109 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5110 var pf = progressEl.dom.firstChild;
5112 pp = Roo.get(pf.firstChild);
5113 pp.setHeight(pf.offsetHeight);
5121 * Updates the message box body text
5122 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5123 * the XHTML-compliant non-breaking space character '&#160;')
5124 * @return {Roo.MessageBox} This message box
5126 updateText : function(text)
5128 if(!dlg.isVisible() && !opt.width){
5129 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5130 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5132 msgEl.innerHTML = text || ' ';
5134 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5135 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5137 Math.min(opt.width || cw , this.maxWidth),
5138 Math.max(opt.minWidth || this.minWidth, bwidth)
5141 activeTextEl.setWidth(w);
5143 if(dlg.isVisible()){
5144 dlg.fixedcenter = false;
5146 // to big, make it scroll. = But as usual stupid IE does not support
5149 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5150 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5151 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5153 bodyEl.dom.style.height = '';
5154 bodyEl.dom.style.overflowY = '';
5157 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5159 bodyEl.dom.style.overflowX = '';
5162 dlg.setContentSize(w, bodyEl.getHeight());
5163 if(dlg.isVisible()){
5164 dlg.fixedcenter = true;
5170 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5171 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5172 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5173 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5174 * @return {Roo.MessageBox} This message box
5176 updateProgress : function(value, text){
5178 this.updateText(text);
5181 if (pp) { // weird bug on my firefox - for some reason this is not defined
5182 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5183 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5189 * Returns true if the message box is currently displayed
5190 * @return {Boolean} True if the message box is visible, else false
5192 isVisible : function(){
5193 return dlg && dlg.isVisible();
5197 * Hides the message box if it is displayed
5200 if(this.isVisible()){
5206 * Displays a new message box, or reinitializes an existing message box, based on the config options
5207 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5208 * The following config object properties are supported:
5210 Property Type Description
5211 ---------- --------------- ------------------------------------------------------------------------------------
5212 animEl String/Element An id or Element from which the message box should animate as it opens and
5213 closes (defaults to undefined)
5214 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5215 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5216 closable Boolean False to hide the top-right close button (defaults to true). Note that
5217 progress and wait dialogs will ignore this property and always hide the
5218 close button as they can only be closed programmatically.
5219 cls String A custom CSS class to apply to the message box element
5220 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5221 displayed (defaults to 75)
5222 fn Function A callback function to execute after closing the dialog. The arguments to the
5223 function will be btn (the name of the button that was clicked, if applicable,
5224 e.g. "ok"), and text (the value of the active text field, if applicable).
5225 Progress and wait dialogs will ignore this option since they do not respond to
5226 user actions and can only be closed programmatically, so any required function
5227 should be called by the same code after it closes the dialog.
5228 icon String A CSS class that provides a background image to be used as an icon for
5229 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5230 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5231 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5232 modal Boolean False to allow user interaction with the page while the message box is
5233 displayed (defaults to true)
5234 msg String A string that will replace the existing message box body text (defaults
5235 to the XHTML-compliant non-breaking space character ' ')
5236 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5237 progress Boolean True to display a progress bar (defaults to false)
5238 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5239 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5240 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5241 title String The title text
5242 value String The string value to set into the active textbox element if displayed
5243 wait Boolean True to display a progress bar (defaults to false)
5244 width Number The width of the dialog in pixels
5251 msg: 'Please enter your address:',
5253 buttons: Roo.MessageBox.OKCANCEL,
5256 animEl: 'addAddressBtn'
5259 * @param {Object} config Configuration options
5260 * @return {Roo.MessageBox} This message box
5262 show : function(options)
5265 // this causes nightmares if you show one dialog after another
5266 // especially on callbacks..
5268 if(this.isVisible()){
5271 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5272 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5273 Roo.log("New Dialog Message:" + options.msg )
5274 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5275 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5278 var d = this.getDialog();
5280 d.setTitle(opt.title || " ");
5281 d.closeEl.setDisplayed(opt.closable !== false);
5282 activeTextEl = textboxEl;
5283 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5288 textareaEl.setHeight(typeof opt.multiline == "number" ?
5289 opt.multiline : this.defaultTextHeight);
5290 activeTextEl = textareaEl;
5299 progressEl.setDisplayed(opt.progress === true);
5301 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5303 this.updateProgress(0);
5304 activeTextEl.dom.value = opt.value || "";
5306 dlg.setDefaultButton(activeTextEl);
5308 var bs = opt.buttons;
5312 }else if(bs && bs.yes){
5313 db = buttons["yes"];
5315 dlg.setDefaultButton(db);
5317 bwidth = updateButtons(opt.buttons);
5318 this.updateText(opt.msg);
5320 d.el.addClass(opt.cls);
5322 d.proxyDrag = opt.proxyDrag === true;
5323 d.modal = opt.modal !== false;
5324 d.mask = opt.modal !== false ? mask : false;
5326 // force it to the end of the z-index stack so it gets a cursor in FF
5327 document.body.appendChild(dlg.el.dom);
5328 d.animateTarget = null;
5329 d.show(options.animEl);
5335 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5336 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5337 * and closing the message box when the process is complete.
5338 * @param {String} title The title bar text
5339 * @param {String} msg The message box body text
5340 * @return {Roo.MessageBox} This message box
5342 progress : function(title, msg){
5349 minWidth: this.minProgressWidth,
5356 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5357 * If a callback function is passed it will be called after the user clicks the button, and the
5358 * id of the button that was clicked will be passed as the only parameter to the callback
5359 * (could also be the top-right close button).
5360 * @param {String} title The title bar text
5361 * @param {String} msg The message box body text
5362 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5363 * @param {Object} scope (optional) The scope of the callback function
5364 * @return {Roo.MessageBox} This message box
5366 alert : function(title, msg, fn, scope)
5381 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5382 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5383 * You are responsible for closing the message box when the process is complete.
5384 * @param {String} msg The message box body text
5385 * @param {String} title (optional) The title bar text
5386 * @return {Roo.MessageBox} This message box
5388 wait : function(msg, title){
5399 waitTimer = Roo.TaskMgr.start({
5401 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5409 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5410 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5411 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5412 * @param {String} title The title bar text
5413 * @param {String} msg The message box body text
5414 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5415 * @param {Object} scope (optional) The scope of the callback function
5416 * @return {Roo.MessageBox} This message box
5418 confirm : function(title, msg, fn, scope){
5422 buttons: this.YESNO,
5431 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5432 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5433 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5434 * (could also be the top-right close button) and the text that was entered will be passed as the two
5435 * parameters to the callback.
5436 * @param {String} title The title bar text
5437 * @param {String} msg The message box body text
5438 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5439 * @param {Object} scope (optional) The scope of the callback function
5440 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5441 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5442 * @return {Roo.MessageBox} This message box
5444 prompt : function(title, msg, fn, scope, multiline){
5448 buttons: this.OKCANCEL,
5453 multiline: multiline,
5460 * Button config that displays a single OK button
5465 * Button config that displays Yes and No buttons
5468 YESNO : {yes:true, no:true},
5470 * Button config that displays OK and Cancel buttons
5473 OKCANCEL : {ok:true, cancel:true},
5475 * Button config that displays Yes, No and Cancel buttons
5478 YESNOCANCEL : {yes:true, no:true, cancel:true},
5481 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5484 defaultTextHeight : 75,
5486 * The maximum width in pixels of the message box (defaults to 600)
5491 * The minimum width in pixels of the message box (defaults to 100)
5496 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5497 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5500 minProgressWidth : 250,
5502 * An object containing the default button text strings that can be overriden for localized language support.
5503 * Supported properties are: ok, cancel, yes and no.
5504 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5517 * Shorthand for {@link Roo.MessageBox}
5519 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5520 Roo.Msg = Roo.Msg || Roo.MessageBox;
5529 * @class Roo.bootstrap.Navbar
5530 * @extends Roo.bootstrap.Component
5531 * Bootstrap Navbar class
5534 * Create a new Navbar
5535 * @param {Object} config The config object
5539 Roo.bootstrap.Navbar = function(config){
5540 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5544 * @event beforetoggle
5545 * Fire before toggle the menu
5546 * @param {Roo.EventObject} e
5548 "beforetoggle" : true
5552 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5561 getAutoCreate : function(){
5564 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5568 initEvents :function ()
5570 //Roo.log(this.el.select('.navbar-toggle',true));
5571 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5578 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5580 var size = this.el.getSize();
5581 this.maskEl.setSize(size.width, size.height);
5582 this.maskEl.enableDisplayMode("block");
5591 getChildContainer : function()
5593 if (this.el && this.el.select('.collapse').getCount()) {
5594 return this.el.select('.collapse',true).first();
5609 onToggle : function()
5612 if(this.fireEvent('beforetoggle', this) === false){
5615 var ce = this.el.select('.navbar-collapse',true).first();
5617 if (!ce.hasClass('show')) {
5627 * Expand the navbar pulldown
5629 expand : function ()
5632 var ce = this.el.select('.navbar-collapse',true).first();
5633 if (ce.hasClass('collapsing')) {
5636 ce.dom.style.height = '';
5638 ce.addClass('in'); // old...
5639 ce.removeClass('collapse');
5640 ce.addClass('show');
5641 var h = ce.getHeight();
5643 ce.removeClass('show');
5644 // at this point we should be able to see it..
5645 ce.addClass('collapsing');
5647 ce.setHeight(0); // resize it ...
5648 ce.on('transitionend', function() {
5649 //Roo.log('done transition');
5650 ce.removeClass('collapsing');
5651 ce.addClass('show');
5652 ce.removeClass('collapse');
5654 ce.dom.style.height = '';
5655 }, this, { single: true} );
5657 ce.dom.scrollTop = 0;
5660 * Collapse the navbar pulldown
5662 collapse : function()
5664 var ce = this.el.select('.navbar-collapse',true).first();
5666 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5667 // it's collapsed or collapsing..
5670 ce.removeClass('in'); // old...
5671 ce.setHeight(ce.getHeight());
5672 ce.removeClass('show');
5673 ce.addClass('collapsing');
5675 ce.on('transitionend', function() {
5676 ce.dom.style.height = '';
5677 ce.removeClass('collapsing');
5678 ce.addClass('collapse');
5679 }, this, { single: true} );
5699 * @class Roo.bootstrap.NavSimplebar
5700 * @extends Roo.bootstrap.Navbar
5701 * Bootstrap Sidebar class
5703 * @cfg {Boolean} inverse is inverted color
5705 * @cfg {String} type (nav | pills | tabs)
5706 * @cfg {Boolean} arrangement stacked | justified
5707 * @cfg {String} align (left | right) alignment
5709 * @cfg {Boolean} main (true|false) main nav bar? default false
5710 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5712 * @cfg {String} tag (header|footer|nav|div) default is nav
5714 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5718 * Create a new Sidebar
5719 * @param {Object} config The config object
5723 Roo.bootstrap.NavSimplebar = function(config){
5724 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5727 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5743 getAutoCreate : function(){
5747 tag : this.tag || 'div',
5748 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5750 if (['light','white'].indexOf(this.weight) > -1) {
5751 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5753 cfg.cls += ' bg-' + this.weight;
5756 cfg.cls += ' navbar-inverse';
5760 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5762 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5771 cls: 'nav nav-' + this.xtype,
5777 this.type = this.type || 'nav';
5778 if (['tabs','pills'].indexOf(this.type) != -1) {
5779 cfg.cn[0].cls += ' nav-' + this.type
5783 if (this.type!=='nav') {
5784 Roo.log('nav type must be nav/tabs/pills')
5786 cfg.cn[0].cls += ' navbar-nav'
5792 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5793 cfg.cn[0].cls += ' nav-' + this.arrangement;
5797 if (this.align === 'right') {
5798 cfg.cn[0].cls += ' navbar-right';
5823 * navbar-expand-md fixed-top
5827 * @class Roo.bootstrap.NavHeaderbar
5828 * @extends Roo.bootstrap.NavSimplebar
5829 * Bootstrap Sidebar class
5831 * @cfg {String} brand what is brand
5832 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5833 * @cfg {String} brand_href href of the brand
5834 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5835 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5836 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5837 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5840 * Create a new Sidebar
5841 * @param {Object} config The config object
5845 Roo.bootstrap.NavHeaderbar = function(config){
5846 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5850 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5857 desktopCenter : false,
5860 getAutoCreate : function(){
5863 tag: this.nav || 'nav',
5864 cls: 'navbar navbar-expand-md',
5870 if (this.desktopCenter) {
5871 cn.push({cls : 'container', cn : []});
5879 cls: 'navbar-toggle navbar-toggler',
5880 'data-toggle': 'collapse',
5885 html: 'Toggle navigation'
5889 cls: 'icon-bar navbar-toggler-icon'
5902 cn.push( Roo.bootstrap.version == 4 ? btn : {
5904 cls: 'navbar-header',
5913 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5917 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5919 if (['light','white'].indexOf(this.weight) > -1) {
5920 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5922 cfg.cls += ' bg-' + this.weight;
5925 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5926 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5928 // tag can override this..
5930 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5933 if (this.brand !== '') {
5934 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5935 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5937 href: this.brand_href ? this.brand_href : '#',
5938 cls: 'navbar-brand',
5946 cfg.cls += ' main-nav';
5954 getHeaderChildContainer : function()
5956 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5957 return this.el.select('.navbar-header',true).first();
5960 return this.getChildContainer();
5963 getChildContainer : function()
5966 return this.el.select('.roo-navbar-collapse',true).first();
5971 initEvents : function()
5973 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5975 if (this.autohide) {
5980 Roo.get(document).on('scroll',function(e) {
5981 var ns = Roo.get(document).getScroll().top;
5982 var os = prevScroll;
5986 ft.removeClass('slideDown');
5987 ft.addClass('slideUp');
5990 ft.removeClass('slideUp');
5991 ft.addClass('slideDown');
6012 * @class Roo.bootstrap.NavSidebar
6013 * @extends Roo.bootstrap.Navbar
6014 * Bootstrap Sidebar class
6017 * Create a new Sidebar
6018 * @param {Object} config The config object
6022 Roo.bootstrap.NavSidebar = function(config){
6023 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6026 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
6028 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6030 getAutoCreate : function(){
6035 cls: 'sidebar sidebar-nav'
6057 * @class Roo.bootstrap.NavGroup
6058 * @extends Roo.bootstrap.Component
6059 * Bootstrap NavGroup class
6060 * @cfg {String} align (left|right)
6061 * @cfg {Boolean} inverse
6062 * @cfg {String} type (nav|pills|tab) default nav
6063 * @cfg {String} navId - reference Id for navbar.
6064 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6067 * Create a new nav group
6068 * @param {Object} config The config object
6071 Roo.bootstrap.NavGroup = function(config){
6072 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6075 Roo.bootstrap.NavGroup.register(this);
6079 * Fires when the active item changes
6080 * @param {Roo.bootstrap.NavGroup} this
6081 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6082 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6089 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6101 getAutoCreate : function()
6103 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6109 if (Roo.bootstrap.version == 4) {
6110 if (['tabs','pills'].indexOf(this.type) != -1) {
6111 cfg.cls += ' nav-' + this.type;
6113 // trying to remove so header bar can right align top?
6114 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6115 // do not use on header bar...
6116 cfg.cls += ' navbar-nav';
6121 if (['tabs','pills'].indexOf(this.type) != -1) {
6122 cfg.cls += ' nav-' + this.type
6124 if (this.type !== 'nav') {
6125 Roo.log('nav type must be nav/tabs/pills')
6127 cfg.cls += ' navbar-nav'
6131 if (this.parent() && this.parent().sidebar) {
6134 cls: 'dashboard-menu sidebar-menu'
6140 if (this.form === true) {
6143 cls: 'navbar-form form-inline'
6145 //nav navbar-right ml-md-auto
6146 if (this.align === 'right') {
6147 cfg.cls += ' navbar-right ml-md-auto';
6149 cfg.cls += ' navbar-left';
6153 if (this.align === 'right') {
6154 cfg.cls += ' navbar-right ml-md-auto';
6156 cfg.cls += ' mr-auto';
6160 cfg.cls += ' navbar-inverse';
6168 * sets the active Navigation item
6169 * @param {Roo.bootstrap.NavItem} the new current navitem
6171 setActiveItem : function(item)
6174 Roo.each(this.navItems, function(v){
6179 v.setActive(false, true);
6186 item.setActive(true, true);
6187 this.fireEvent('changed', this, item, prev);
6192 * gets the active Navigation item
6193 * @return {Roo.bootstrap.NavItem} the current navitem
6195 getActive : function()
6199 Roo.each(this.navItems, function(v){
6210 indexOfNav : function()
6214 Roo.each(this.navItems, function(v,i){
6225 * adds a Navigation item
6226 * @param {Roo.bootstrap.NavItem} the navitem to add
6228 addItem : function(cfg)
6230 if (this.form && Roo.bootstrap.version == 4) {
6233 var cn = new Roo.bootstrap.NavItem(cfg);
6235 cn.parentId = this.id;
6236 cn.onRender(this.el, null);
6240 * register a Navigation item
6241 * @param {Roo.bootstrap.NavItem} the navitem to add
6243 register : function(item)
6245 this.navItems.push( item);
6246 item.navId = this.navId;
6251 * clear all the Navigation item
6254 clearAll : function()
6257 this.el.dom.innerHTML = '';
6260 getNavItem: function(tabId)
6263 Roo.each(this.navItems, function(e) {
6264 if (e.tabId == tabId) {
6274 setActiveNext : function()
6276 var i = this.indexOfNav(this.getActive());
6277 if (i > this.navItems.length) {
6280 this.setActiveItem(this.navItems[i+1]);
6282 setActivePrev : function()
6284 var i = this.indexOfNav(this.getActive());
6288 this.setActiveItem(this.navItems[i-1]);
6290 clearWasActive : function(except) {
6291 Roo.each(this.navItems, function(e) {
6292 if (e.tabId != except.tabId && e.was_active) {
6293 e.was_active = false;
6300 getWasActive : function ()
6303 Roo.each(this.navItems, function(e) {
6318 Roo.apply(Roo.bootstrap.NavGroup, {
6322 * register a Navigation Group
6323 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6325 register : function(navgrp)
6327 this.groups[navgrp.navId] = navgrp;
6331 * fetch a Navigation Group based on the navigation ID
6332 * @param {string} the navgroup to add
6333 * @returns {Roo.bootstrap.NavGroup} the navgroup
6335 get: function(navId) {
6336 if (typeof(this.groups[navId]) == 'undefined') {
6338 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6340 return this.groups[navId] ;
6355 * @class Roo.bootstrap.NavItem
6356 * @extends Roo.bootstrap.Component
6357 * Bootstrap Navbar.NavItem class
6358 * @cfg {String} href link to
6359 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6360 * @cfg {Boolean} button_outline show and outlined button
6361 * @cfg {String} html content of button
6362 * @cfg {String} badge text inside badge
6363 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6364 * @cfg {String} glyphicon DEPRICATED - use fa
6365 * @cfg {String} icon DEPRICATED - use fa
6366 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6367 * @cfg {Boolean} active Is item active
6368 * @cfg {Boolean} disabled Is item disabled
6369 * @cfg {String} linkcls Link Class
6370 * @cfg {Boolean} preventDefault (true | false) default false
6371 * @cfg {String} tabId the tab that this item activates.
6372 * @cfg {String} tagtype (a|span) render as a href or span?
6373 * @cfg {Boolean} animateRef (true|false) link to element default false
6376 * Create a new Navbar Item
6377 * @param {Object} config The config object
6379 Roo.bootstrap.NavItem = function(config){
6380 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6385 * The raw click event for the entire grid.
6386 * @param {Roo.EventObject} e
6391 * Fires when the active item active state changes
6392 * @param {Roo.bootstrap.NavItem} this
6393 * @param {boolean} state the new state
6399 * Fires when scroll to element
6400 * @param {Roo.bootstrap.NavItem} this
6401 * @param {Object} options
6402 * @param {Roo.EventObject} e
6410 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6419 preventDefault : false,
6427 button_outline : false,
6431 getAutoCreate : function(){
6438 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6441 cfg.cls += ' active' ;
6443 if (this.disabled) {
6444 cfg.cls += ' disabled';
6448 if (this.button_weight.length) {
6449 cfg.tag = this.href ? 'a' : 'button';
6450 cfg.html = this.html || '';
6451 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6453 cfg.href = this.href;
6456 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6458 cfg.cls += " nav-html";
6461 // menu .. should add dropdown-menu class - so no need for carat..
6463 if (this.badge !== '') {
6465 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6470 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6474 href : this.href || "#",
6475 html: this.html || '',
6479 if (this.tagtype == 'a') {
6480 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6484 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6485 } else if (this.fa) {
6486 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6487 } else if(this.glyphicon) {
6488 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6490 cfg.cn[0].cls += " nav-html";
6494 cfg.cn[0].html += " <span class='caret'></span>";
6498 if (this.badge !== '') {
6499 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6507 onRender : function(ct, position)
6509 // Roo.log("Call onRender: " + this.xtype);
6510 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6514 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6515 this.navLink = this.el.select('.nav-link',true).first();
6516 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
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.tagtype == 'span'){
6532 // this.el.select('span',true).on('click', this.onClick, this);
6535 // at this point parent should be available..
6536 this.parent().register(this);
6539 onClick : function(e)
6541 if (e.getTarget('.dropdown-menu-item')) {
6542 // did you click on a menu itemm.... - then don't trigger onclick..
6547 this.preventDefault ||
6550 Roo.log("NavItem - prevent Default?");
6554 if (this.disabled) {
6558 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6559 if (tg && tg.transition) {
6560 Roo.log("waiting for the transitionend");
6566 //Roo.log("fire event clicked");
6567 if(this.fireEvent('click', this, e) === false){
6571 if(this.tagtype == 'span'){
6575 //Roo.log(this.href);
6576 var ael = this.el.select('a',true).first();
6579 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6580 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6581 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6582 return; // ignore... - it's a 'hash' to another page.
6584 Roo.log("NavItem - prevent Default?");
6586 this.scrollToElement(e);
6590 var p = this.parent();
6592 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6593 if (typeof(p.setActiveItem) !== 'undefined') {
6594 p.setActiveItem(this);
6598 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6599 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6600 // remove the collapsed menu expand...
6601 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6605 isActive: function () {
6608 setActive : function(state, fire, is_was_active)
6610 if (this.active && !state && this.navId) {
6611 this.was_active = true;
6612 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6614 nv.clearWasActive(this);
6618 this.active = state;
6621 this.el.removeClass('active');
6622 this.navLink ? this.navLink.removeClass('active') : false;
6623 } else if (!this.el.hasClass('active')) {
6625 this.el.addClass('active');
6626 if (Roo.bootstrap.version == 4 && this.navLink ) {
6627 this.navLink.addClass('active');
6632 this.fireEvent('changed', this, state);
6635 // show a panel if it's registered and related..
6637 if (!this.navId || !this.tabId || !state || is_was_active) {
6641 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6645 var pan = tg.getPanelByName(this.tabId);
6649 // if we can not flip to new panel - go back to old nav highlight..
6650 if (false == tg.showPanel(pan)) {
6651 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6653 var onav = nv.getWasActive();
6655 onav.setActive(true, false, true);
6664 // this should not be here...
6665 setDisabled : function(state)
6667 this.disabled = state;
6669 this.el.removeClass('disabled');
6670 } else if (!this.el.hasClass('disabled')) {
6671 this.el.addClass('disabled');
6677 * Fetch the element to display the tooltip on.
6678 * @return {Roo.Element} defaults to this.el
6680 tooltipEl : function()
6682 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6685 scrollToElement : function(e)
6687 var c = document.body;
6690 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6692 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6693 c = document.documentElement;
6696 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6702 var o = target.calcOffsetsTo(c);
6709 this.fireEvent('scrollto', this, options, e);
6711 Roo.get(c).scrollTo('top', options.value, true);
6716 * Set the HTML (text content) of the item
6717 * @param {string} html content for the nav item
6719 setHtml : function(html)
6722 this.htmlEl.dom.innerHTML = html;
6734 * <span> icon </span>
6735 * <span> text </span>
6736 * <span>badge </span>
6740 * @class Roo.bootstrap.NavSidebarItem
6741 * @extends Roo.bootstrap.NavItem
6742 * Bootstrap Navbar.NavSidebarItem class
6743 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6744 * {Boolean} open is the menu open
6745 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6746 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6747 * {String} buttonSize (sm|md|lg)the extra classes for the button
6748 * {Boolean} showArrow show arrow next to the text (default true)
6750 * Create a new Navbar Button
6751 * @param {Object} config The config object
6753 Roo.bootstrap.NavSidebarItem = function(config){
6754 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6759 * The raw click event for the entire grid.
6760 * @param {Roo.EventObject} e
6765 * Fires when the active item active state changes
6766 * @param {Roo.bootstrap.NavSidebarItem} this
6767 * @param {boolean} state the new state
6775 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6777 badgeWeight : 'default',
6783 buttonWeight : 'default',
6789 getAutoCreate : function(){
6794 href : this.href || '#',
6800 if(this.buttonView){
6803 href : this.href || '#',
6804 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6817 cfg.cls += ' active';
6820 if (this.disabled) {
6821 cfg.cls += ' disabled';
6824 cfg.cls += ' open x-open';
6827 if (this.glyphicon || this.icon) {
6828 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6829 a.cn.push({ tag : 'i', cls : c }) ;
6832 if(!this.buttonView){
6835 html : this.html || ''
6842 if (this.badge !== '') {
6843 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6849 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6852 a.cls += ' dropdown-toggle treeview' ;
6858 initEvents : function()
6860 if (typeof (this.menu) != 'undefined') {
6861 this.menu.parentType = this.xtype;
6862 this.menu.triggerEl = this.el;
6863 this.menu = this.addxtype(Roo.apply({}, this.menu));
6866 this.el.on('click', this.onClick, this);
6868 if(this.badge !== ''){
6869 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6874 onClick : function(e)
6881 if(this.preventDefault){
6885 this.fireEvent('click', this, e);
6888 disable : function()
6890 this.setDisabled(true);
6895 this.setDisabled(false);
6898 setDisabled : function(state)
6900 if(this.disabled == state){
6904 this.disabled = state;
6907 this.el.addClass('disabled');
6911 this.el.removeClass('disabled');
6916 setActive : function(state)
6918 if(this.active == state){
6922 this.active = state;
6925 this.el.addClass('active');
6929 this.el.removeClass('active');
6934 isActive: function ()
6939 setBadge : function(str)
6945 this.badgeEl.dom.innerHTML = str;
6960 Roo.namespace('Roo.bootstrap.breadcrumb');
6964 * @class Roo.bootstrap.breadcrumb.Nav
6965 * @extends Roo.bootstrap.Component
6966 * Bootstrap Breadcrumb Nav Class
6968 * @children Roo.bootstrap.breadcrumb.Item
6971 * Create a new breadcrumb.Nav
6972 * @param {Object} config The config object
6976 Roo.bootstrap.breadcrumb.Nav = function(config){
6977 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6982 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6984 getAutoCreate : function()
7001 initEvents: function()
7003 this.olEl = this.el.select('ol',true).first();
7005 getChildContainer : function()
7021 * @class Roo.bootstrap.breadcrumb.Nav
7022 * @extends Roo.bootstrap.Component
7023 * Bootstrap Breadcrumb Nav Class
7025 * @children Roo.bootstrap.breadcrumb.Component
7026 * @cfg {String} html the content of the link.
7027 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7028 * @cfg {Boolean} active is it active
7032 * Create a new breadcrumb.Nav
7033 * @param {Object} config The config object
7036 Roo.bootstrap.breadcrumb.Item = function(config){
7037 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7042 * The img click event for the img.
7043 * @param {Roo.EventObject} e
7050 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7055 getAutoCreate : function()
7060 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7062 if (this.href !== false) {
7069 cfg.html = this.html;
7075 initEvents: function()
7078 this.el.select('a', true).first().on('click',this.onClick, this)
7082 onClick : function(e)
7085 this.fireEvent('click',this, e);
7098 * @class Roo.bootstrap.Row
7099 * @extends Roo.bootstrap.Component
7100 * @children Roo.bootstrap.Component
7101 * Bootstrap Row class (contains columns...)
7105 * @param {Object} config The config object
7108 Roo.bootstrap.Row = function(config){
7109 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7112 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7114 getAutoCreate : function(){
7133 * @class Roo.bootstrap.Pagination
7134 * @extends Roo.bootstrap.Component
7135 * Bootstrap Pagination class
7136 * @cfg {String} size xs | sm | md | lg
7137 * @cfg {Boolean} inverse false | true
7140 * Create a new Pagination
7141 * @param {Object} config The config object
7144 Roo.bootstrap.Pagination = function(config){
7145 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7148 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7154 getAutoCreate : function(){
7160 cfg.cls += ' inverse';
7166 cfg.cls += " " + this.cls;
7184 * @class Roo.bootstrap.PaginationItem
7185 * @extends Roo.bootstrap.Component
7186 * Bootstrap PaginationItem class
7187 * @cfg {String} html text
7188 * @cfg {String} href the link
7189 * @cfg {Boolean} preventDefault (true | false) default true
7190 * @cfg {Boolean} active (true | false) default false
7191 * @cfg {Boolean} disabled default false
7195 * Create a new PaginationItem
7196 * @param {Object} config The config object
7200 Roo.bootstrap.PaginationItem = function(config){
7201 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7206 * The raw click event for the entire grid.
7207 * @param {Roo.EventObject} e
7213 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7217 preventDefault: true,
7222 getAutoCreate : function(){
7228 href : this.href ? this.href : '#',
7229 html : this.html ? this.html : ''
7239 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7243 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7249 initEvents: function() {
7251 this.el.on('click', this.onClick, this);
7254 onClick : function(e)
7256 Roo.log('PaginationItem on click ');
7257 if(this.preventDefault){
7265 this.fireEvent('click', this, e);
7281 * @class Roo.bootstrap.Slider
7282 * @extends Roo.bootstrap.Component
7283 * Bootstrap Slider class
7286 * Create a new Slider
7287 * @param {Object} config The config object
7290 Roo.bootstrap.Slider = function(config){
7291 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7294 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7296 getAutoCreate : function(){
7300 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7304 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7316 * Ext JS Library 1.1.1
7317 * Copyright(c) 2006-2007, Ext JS, LLC.
7319 * Originally Released Under LGPL - original licence link has changed is not relivant.
7322 * <script type="text/javascript">
7325 * @extends Roo.dd.DDProxy
7326 * @class Roo.grid.SplitDragZone
7327 * Support for Column Header resizing
7329 * @param {Object} config
7332 // This is a support class used internally by the Grid components
7333 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7335 this.view = grid.getView();
7336 this.proxy = this.view.resizeProxy;
7337 Roo.grid.SplitDragZone.superclass.constructor.call(
7340 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7342 dragElId : Roo.id(this.proxy.dom),
7347 this.setHandleElId(Roo.id(hd));
7348 if (hd2 !== false) {
7349 this.setOuterHandleElId(Roo.id(hd2));
7352 this.scroll = false;
7354 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7355 fly: Roo.Element.fly,
7357 b4StartDrag : function(x, y){
7358 this.view.headersDisabled = true;
7359 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7360 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7362 this.proxy.setHeight(h);
7364 // for old system colWidth really stored the actual width?
7365 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7366 // which in reality did not work.. - it worked only for fixed sizes
7367 // for resizable we need to use actual sizes.
7368 var w = this.cm.getColumnWidth(this.cellIndex);
7369 if (!this.view.mainWrap) {
7371 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7376 // this was w-this.grid.minColumnWidth;
7377 // doesnt really make sense? - w = thie curren width or the rendered one?
7378 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7379 this.resetConstraints();
7380 this.setXConstraint(minw, 1000);
7381 this.setYConstraint(0, 0);
7382 this.minX = x - minw;
7383 this.maxX = x + 1000;
7385 if (!this.view.mainWrap) { // this is Bootstrap code..
7386 this.getDragEl().style.display='block';
7389 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7393 handleMouseDown : function(e){
7394 ev = Roo.EventObject.setEvent(e);
7395 var t = this.fly(ev.getTarget());
7396 if(t.hasClass("x-grid-split")){
7397 this.cellIndex = this.view.getCellIndex(t.dom);
7399 this.cm = this.grid.colModel;
7400 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7401 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7406 endDrag : function(e){
7407 this.view.headersDisabled = false;
7408 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7409 var diff = endX - this.startPos;
7411 var w = this.cm.getColumnWidth(this.cellIndex);
7412 if (!this.view.mainWrap) {
7415 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7418 autoOffset : function(){
7423 * Ext JS Library 1.1.1
7424 * Copyright(c) 2006-2007, Ext JS, LLC.
7426 * Originally Released Under LGPL - original licence link has changed is not relivant.
7429 * <script type="text/javascript">
7433 * @class Roo.grid.AbstractSelectionModel
7434 * @extends Roo.util.Observable
7436 * Abstract base class for grid SelectionModels. It provides the interface that should be
7437 * implemented by descendant classes. This class should not be directly instantiated.
7440 Roo.grid.AbstractSelectionModel = function(){
7441 this.locked = false;
7442 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7445 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7446 /** @ignore Called by the grid automatically. Do not call directly. */
7447 init : function(grid){
7453 * Locks the selections.
7460 * Unlocks the selections.
7462 unlock : function(){
7463 this.locked = false;
7467 * Returns true if the selections are locked.
7470 isLocked : function(){
7475 * Ext JS Library 1.1.1
7476 * Copyright(c) 2006-2007, Ext JS, LLC.
7478 * Originally Released Under LGPL - original licence link has changed is not relivant.
7481 * <script type="text/javascript">
7484 * @extends Roo.grid.AbstractSelectionModel
7485 * @class Roo.grid.RowSelectionModel
7486 * The default SelectionModel used by {@link Roo.grid.Grid}.
7487 * It supports multiple selections and keyboard selection/navigation.
7489 * @param {Object} config
7491 Roo.grid.RowSelectionModel = function(config){
7492 Roo.apply(this, config);
7493 this.selections = new Roo.util.MixedCollection(false, function(o){
7498 this.lastActive = false;
7502 * @event selectionchange
7503 * Fires when the selection changes
7504 * @param {SelectionModel} this
7506 "selectionchange" : true,
7508 * @event afterselectionchange
7509 * Fires after the selection changes (eg. by key press or clicking)
7510 * @param {SelectionModel} this
7512 "afterselectionchange" : true,
7514 * @event beforerowselect
7515 * Fires when a row is selected being selected, return false to cancel.
7516 * @param {SelectionModel} this
7517 * @param {Number} rowIndex The selected index
7518 * @param {Boolean} keepExisting False if other selections will be cleared
7520 "beforerowselect" : true,
7523 * Fires when a row is selected.
7524 * @param {SelectionModel} this
7525 * @param {Number} rowIndex The selected index
7526 * @param {Roo.data.Record} r The record
7530 * @event rowdeselect
7531 * Fires when a row is deselected.
7532 * @param {SelectionModel} this
7533 * @param {Number} rowIndex The selected index
7535 "rowdeselect" : true
7537 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7538 this.locked = false;
7541 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7543 * @cfg {Boolean} singleSelect
7544 * True to allow selection of only one row at a time (defaults to false)
7546 singleSelect : false,
7549 initEvents : function(){
7551 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7552 this.grid.on("mousedown", this.handleMouseDown, this);
7553 }else{ // allow click to work like normal
7554 this.grid.on("rowclick", this.handleDragableRowClick, this);
7556 // bootstrap does not have a view..
7557 var view = this.grid.view ? this.grid.view : this.grid;
7558 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7561 this.selectPrevious(e.shiftKey);
7562 }else if(this.last !== false && this.lastActive !== false){
7563 var last = this.last;
7564 this.selectRange(this.last, this.lastActive-1);
7565 view.focusRow(this.lastActive);
7570 this.selectFirstRow();
7572 this.fireEvent("afterselectionchange", this);
7574 "down" : function(e){
7576 this.selectNext(e.shiftKey);
7577 }else if(this.last !== false && this.lastActive !== false){
7578 var last = this.last;
7579 this.selectRange(this.last, this.lastActive+1);
7580 view.focusRow(this.lastActive);
7585 this.selectFirstRow();
7587 this.fireEvent("afterselectionchange", this);
7593 view.on("refresh", this.onRefresh, this);
7594 view.on("rowupdated", this.onRowUpdated, this);
7595 view.on("rowremoved", this.onRemove, this);
7599 onRefresh : function(){
7600 var ds = this.grid.ds, i, v = this.grid.view;
7601 var s = this.selections;
7603 if((i = ds.indexOfId(r.id)) != -1){
7605 s.add(ds.getAt(i)); // updating the selection relate data
7613 onRemove : function(v, index, r){
7614 this.selections.remove(r);
7618 onRowUpdated : function(v, index, r){
7619 if(this.isSelected(r)){
7620 v.onRowSelect(index);
7626 * @param {Array} records The records to select
7627 * @param {Boolean} keepExisting (optional) True to keep existing selections
7629 selectRecords : function(records, keepExisting){
7631 this.clearSelections();
7633 var ds = this.grid.ds;
7634 for(var i = 0, len = records.length; i < len; i++){
7635 this.selectRow(ds.indexOf(records[i]), true);
7640 * Gets the number of selected rows.
7643 getCount : function(){
7644 return this.selections.length;
7648 * Selects the first row in the grid.
7650 selectFirstRow : function(){
7655 * Select the last row.
7656 * @param {Boolean} keepExisting (optional) True to keep existing selections
7658 selectLastRow : function(keepExisting){
7659 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7663 * Selects the row immediately following the last selected row.
7664 * @param {Boolean} keepExisting (optional) True to keep existing selections
7666 selectNext : function(keepExisting){
7667 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7668 this.selectRow(this.last+1, keepExisting);
7669 var view = this.grid.view ? this.grid.view : this.grid;
7670 view.focusRow(this.last);
7675 * Selects the row that precedes the last selected row.
7676 * @param {Boolean} keepExisting (optional) True to keep existing selections
7678 selectPrevious : function(keepExisting){
7680 this.selectRow(this.last-1, keepExisting);
7681 var view = this.grid.view ? this.grid.view : this.grid;
7682 view.focusRow(this.last);
7687 * Returns the selected records
7688 * @return {Array} Array of selected records
7690 getSelections : function(){
7691 return [].concat(this.selections.items);
7695 * Returns the first selected record.
7698 getSelected : function(){
7699 return this.selections.itemAt(0);
7704 * Clears all selections.
7706 clearSelections : function(fast){
7711 var ds = this.grid.ds;
7712 var s = this.selections;
7714 this.deselectRow(ds.indexOfId(r.id));
7718 this.selections.clear();
7727 selectAll : function(){
7731 this.selections.clear();
7732 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7733 this.selectRow(i, true);
7738 * Returns True if there is a selection.
7741 hasSelection : function(){
7742 return this.selections.length > 0;
7746 * Returns True if the specified row is selected.
7747 * @param {Number/Record} record The record or index of the record to check
7750 isSelected : function(index){
7751 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7752 return (r && this.selections.key(r.id) ? true : false);
7756 * Returns True if the specified record id is selected.
7757 * @param {String} id The id of record to check
7760 isIdSelected : function(id){
7761 return (this.selections.key(id) ? true : false);
7765 handleMouseDown : function(e, t)
7767 var view = this.grid.view ? this.grid.view : this.grid;
7769 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7772 if(e.shiftKey && this.last !== false){
7773 var last = this.last;
7774 this.selectRange(last, rowIndex, e.ctrlKey);
7775 this.last = last; // reset the last
7776 view.focusRow(rowIndex);
7778 var isSelected = this.isSelected(rowIndex);
7779 if(e.button !== 0 && isSelected){
7780 view.focusRow(rowIndex);
7781 }else if(e.ctrlKey && isSelected){
7782 this.deselectRow(rowIndex);
7783 }else if(!isSelected){
7784 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7785 view.focusRow(rowIndex);
7788 this.fireEvent("afterselectionchange", this);
7791 handleDragableRowClick : function(grid, rowIndex, e)
7793 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7794 this.selectRow(rowIndex, false);
7795 var view = this.grid.view ? this.grid.view : this.grid;
7796 view.focusRow(rowIndex);
7797 this.fireEvent("afterselectionchange", this);
7802 * Selects multiple rows.
7803 * @param {Array} rows Array of the indexes of the row to select
7804 * @param {Boolean} keepExisting (optional) True to keep existing selections
7806 selectRows : function(rows, keepExisting){
7808 this.clearSelections();
7810 for(var i = 0, len = rows.length; i < len; i++){
7811 this.selectRow(rows[i], true);
7816 * Selects a range of rows. All rows in between startRow and endRow are also selected.
7817 * @param {Number} startRow The index of the first row in the range
7818 * @param {Number} endRow The index of the last row in the range
7819 * @param {Boolean} keepExisting (optional) True to retain existing selections
7821 selectRange : function(startRow, endRow, keepExisting){
7826 this.clearSelections();
7828 if(startRow <= endRow){
7829 for(var i = startRow; i <= endRow; i++){
7830 this.selectRow(i, true);
7833 for(var i = startRow; i >= endRow; i--){
7834 this.selectRow(i, true);
7840 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7841 * @param {Number} startRow The index of the first row in the range
7842 * @param {Number} endRow The index of the last row in the range
7844 deselectRange : function(startRow, endRow, preventViewNotify){
7848 for(var i = startRow; i <= endRow; i++){
7849 this.deselectRow(i, preventViewNotify);
7855 * @param {Number} row The index of the row to select
7856 * @param {Boolean} keepExisting (optional) True to keep existing selections
7858 selectRow : function(index, keepExisting, preventViewNotify){
7859 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7862 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7863 if(!keepExisting || this.singleSelect){
7864 this.clearSelections();
7866 var r = this.grid.ds.getAt(index);
7867 this.selections.add(r);
7868 this.last = this.lastActive = index;
7869 if(!preventViewNotify){
7870 var view = this.grid.view ? this.grid.view : this.grid;
7871 view.onRowSelect(index);
7873 this.fireEvent("rowselect", this, index, r);
7874 this.fireEvent("selectionchange", this);
7880 * @param {Number} row The index of the row to deselect
7882 deselectRow : function(index, preventViewNotify){
7886 if(this.last == index){
7889 if(this.lastActive == index){
7890 this.lastActive = false;
7892 var r = this.grid.ds.getAt(index);
7893 this.selections.remove(r);
7894 if(!preventViewNotify){
7895 var view = this.grid.view ? this.grid.view : this.grid;
7896 view.onRowDeselect(index);
7898 this.fireEvent("rowdeselect", this, index);
7899 this.fireEvent("selectionchange", this);
7903 restoreLast : function(){
7905 this.last = this._last;
7910 acceptsNav : function(row, col, cm){
7911 return !cm.isHidden(col) && cm.isCellEditable(col, row);
7915 onEditorKey : function(field, e){
7916 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7921 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7923 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7925 }else if(k == e.ENTER && !e.ctrlKey){
7929 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7931 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7933 }else if(k == e.ESC){
7937 g.startEditing(newCell[0], newCell[1]);
7942 * Ext JS Library 1.1.1
7943 * Copyright(c) 2006-2007, Ext JS, LLC.
7945 * Originally Released Under LGPL - original licence link has changed is not relivant.
7948 * <script type="text/javascript">
7953 * @class Roo.grid.ColumnModel
7954 * @extends Roo.util.Observable
7955 * This is the default implementation of a ColumnModel used by the Grid. It defines
7956 * the columns in the grid.
7959 var colModel = new Roo.grid.ColumnModel([
7960 {header: "Ticker", width: 60, sortable: true, locked: true},
7961 {header: "Company Name", width: 150, sortable: true},
7962 {header: "Market Cap.", width: 100, sortable: true},
7963 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7964 {header: "Employees", width: 100, sortable: true, resizable: false}
7969 * The config options listed for this class are options which may appear in each
7970 * individual column definition.
7971 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7973 * @param {Object} config An Array of column config objects. See this class's
7974 * config objects for details.
7976 Roo.grid.ColumnModel = function(config){
7978 * The config passed into the constructor
7980 this.config = []; //config;
7983 // if no id, create one
7984 // if the column does not have a dataIndex mapping,
7985 // map it to the order it is in the config
7986 for(var i = 0, len = config.length; i < len; i++){
7987 this.addColumn(config[i]);
7992 * The width of columns which have no width specified (defaults to 100)
7995 this.defaultWidth = 100;
7998 * Default sortable of columns which have no sortable specified (defaults to false)
8001 this.defaultSortable = false;
8005 * @event widthchange
8006 * Fires when the width of a column changes.
8007 * @param {ColumnModel} this
8008 * @param {Number} columnIndex The column index
8009 * @param {Number} newWidth The new width
8011 "widthchange": true,
8013 * @event headerchange
8014 * Fires when the text of a header changes.
8015 * @param {ColumnModel} this
8016 * @param {Number} columnIndex The column index
8017 * @param {Number} newText The new header text
8019 "headerchange": true,
8021 * @event hiddenchange
8022 * Fires when a column is hidden or "unhidden".
8023 * @param {ColumnModel} this
8024 * @param {Number} columnIndex The column index
8025 * @param {Boolean} hidden true if hidden, false otherwise
8027 "hiddenchange": true,
8029 * @event columnmoved
8030 * Fires when a column is moved.
8031 * @param {ColumnModel} this
8032 * @param {Number} oldIndex
8033 * @param {Number} newIndex
8035 "columnmoved" : true,
8037 * @event columlockchange
8038 * Fires when a column's locked state is changed
8039 * @param {ColumnModel} this
8040 * @param {Number} colIndex
8041 * @param {Boolean} locked true if locked
8043 "columnlockchange" : true
8045 Roo.grid.ColumnModel.superclass.constructor.call(this);
8047 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8049 * @cfg {String} header The header text to display in the Grid view.
8052 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8055 * @cfg {String} smHeader Header at Bootsrap Small width
8058 * @cfg {String} mdHeader Header at Bootsrap Medium width
8061 * @cfg {String} lgHeader Header at Bootsrap Large width
8064 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8067 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8068 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8069 * specified, the column's index is used as an index into the Record's data Array.
8072 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8073 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8076 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8077 * Defaults to the value of the {@link #defaultSortable} property.
8078 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8081 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
8084 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
8087 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8090 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8093 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8094 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8095 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8096 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8099 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
8102 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
8105 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
8108 * @cfg {String} cursor (Optional)
8111 * @cfg {String} tooltip (Optional)
8114 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8117 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8120 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8123 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8126 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8129 * Returns the id of the column at the specified index.
8130 * @param {Number} index The column index
8131 * @return {String} the id
8133 getColumnId : function(index){
8134 return this.config[index].id;
8138 * Returns the column for a specified id.
8139 * @param {String} id The column id
8140 * @return {Object} the column
8142 getColumnById : function(id){
8143 return this.lookup[id];
8148 * Returns the column Object for a specified dataIndex.
8149 * @param {String} dataIndex The column dataIndex
8150 * @return {Object|Boolean} the column or false if not found
8152 getColumnByDataIndex: function(dataIndex){
8153 var index = this.findColumnIndex(dataIndex);
8154 return index > -1 ? this.config[index] : false;
8158 * Returns the index for a specified column id.
8159 * @param {String} id The column id
8160 * @return {Number} the index, or -1 if not found
8162 getIndexById : function(id){
8163 for(var i = 0, len = this.config.length; i < len; i++){
8164 if(this.config[i].id == id){
8172 * Returns the index for a specified column dataIndex.
8173 * @param {String} dataIndex The column dataIndex
8174 * @return {Number} the index, or -1 if not found
8177 findColumnIndex : function(dataIndex){
8178 for(var i = 0, len = this.config.length; i < len; i++){
8179 if(this.config[i].dataIndex == dataIndex){
8187 moveColumn : function(oldIndex, newIndex){
8188 var c = this.config[oldIndex];
8189 this.config.splice(oldIndex, 1);
8190 this.config.splice(newIndex, 0, c);
8191 this.dataMap = null;
8192 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8195 isLocked : function(colIndex){
8196 return this.config[colIndex].locked === true;
8199 setLocked : function(colIndex, value, suppressEvent){
8200 if(this.isLocked(colIndex) == value){
8203 this.config[colIndex].locked = value;
8205 this.fireEvent("columnlockchange", this, colIndex, value);
8209 getTotalLockedWidth : function(){
8211 for(var i = 0; i < this.config.length; i++){
8212 if(this.isLocked(i) && !this.isHidden(i)){
8213 this.totalWidth += this.getColumnWidth(i);
8219 getLockedCount : function(){
8220 for(var i = 0, len = this.config.length; i < len; i++){
8221 if(!this.isLocked(i)){
8226 return this.config.length;
8230 * Returns the number of columns.
8233 getColumnCount : function(visibleOnly){
8234 if(visibleOnly === true){
8236 for(var i = 0, len = this.config.length; i < len; i++){
8237 if(!this.isHidden(i)){
8243 return this.config.length;
8247 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8248 * @param {Function} fn
8249 * @param {Object} scope (optional)
8250 * @return {Array} result
8252 getColumnsBy : function(fn, scope){
8254 for(var i = 0, len = this.config.length; i < len; i++){
8255 var c = this.config[i];
8256 if(fn.call(scope||this, c, i) === true){
8264 * Returns true if the specified column is sortable.
8265 * @param {Number} col The column index
8268 isSortable : function(col){
8269 if(typeof this.config[col].sortable == "undefined"){
8270 return this.defaultSortable;
8272 return this.config[col].sortable;
8276 * Returns the rendering (formatting) function defined for the column.
8277 * @param {Number} col The column index.
8278 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8280 getRenderer : function(col){
8281 if(!this.config[col].renderer){
8282 return Roo.grid.ColumnModel.defaultRenderer;
8284 return this.config[col].renderer;
8288 * Sets the rendering (formatting) function for a column.
8289 * @param {Number} col The column index
8290 * @param {Function} fn The function to use to process the cell's raw data
8291 * to return HTML markup for the grid view. The render function is called with
8292 * the following parameters:<ul>
8293 * <li>Data value.</li>
8294 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8295 * <li>css A CSS style string to apply to the table cell.</li>
8296 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8297 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8298 * <li>Row index</li>
8299 * <li>Column index</li>
8300 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8302 setRenderer : function(col, fn){
8303 this.config[col].renderer = fn;
8307 * Returns the width for the specified column.
8308 * @param {Number} col The column index
8309 * @param (optional) {String} gridSize bootstrap width size.
8312 getColumnWidth : function(col, gridSize)
8314 var cfg = this.config[col];
8316 if (typeof(gridSize) == 'undefined') {
8317 return cfg.width * 1 || this.defaultWidth;
8319 if (gridSize === false) { // if we set it..
8320 return cfg.width || false;
8322 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8324 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8325 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8328 return cfg[ sizes[i] ];
8335 * Sets the width for a column.
8336 * @param {Number} col The column index
8337 * @param {Number} width The new width
8339 setColumnWidth : function(col, width, suppressEvent){
8340 this.config[col].width = width;
8341 this.totalWidth = null;
8343 this.fireEvent("widthchange", this, col, width);
8348 * Returns the total width of all columns.
8349 * @param {Boolean} includeHidden True to include hidden column widths
8352 getTotalWidth : function(includeHidden){
8353 if(!this.totalWidth){
8354 this.totalWidth = 0;
8355 for(var i = 0, len = this.config.length; i < len; i++){
8356 if(includeHidden || !this.isHidden(i)){
8357 this.totalWidth += this.getColumnWidth(i);
8361 return this.totalWidth;
8365 * Returns the header for the specified column.
8366 * @param {Number} col The column index
8369 getColumnHeader : function(col){
8370 return this.config[col].header;
8374 * Sets the header for a column.
8375 * @param {Number} col The column index
8376 * @param {String} header The new header
8378 setColumnHeader : function(col, header){
8379 this.config[col].header = header;
8380 this.fireEvent("headerchange", this, col, header);
8384 * Returns the tooltip for the specified column.
8385 * @param {Number} col The column index
8388 getColumnTooltip : function(col){
8389 return this.config[col].tooltip;
8392 * Sets the tooltip for a column.
8393 * @param {Number} col The column index
8394 * @param {String} tooltip The new tooltip
8396 setColumnTooltip : function(col, tooltip){
8397 this.config[col].tooltip = tooltip;
8401 * Returns the dataIndex for the specified column.
8402 * @param {Number} col The column index
8405 getDataIndex : function(col){
8406 return this.config[col].dataIndex;
8410 * Sets the dataIndex for a column.
8411 * @param {Number} col The column index
8412 * @param {Number} dataIndex The new dataIndex
8414 setDataIndex : function(col, dataIndex){
8415 this.config[col].dataIndex = dataIndex;
8421 * Returns true if the cell is editable.
8422 * @param {Number} colIndex The column index
8423 * @param {Number} rowIndex The row index - this is nto actually used..?
8426 isCellEditable : function(colIndex, rowIndex){
8427 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8431 * Returns the editor defined for the cell/column.
8432 * return false or null to disable editing.
8433 * @param {Number} colIndex The column index
8434 * @param {Number} rowIndex The row index
8437 getCellEditor : function(colIndex, rowIndex){
8438 return this.config[colIndex].editor;
8442 * Sets if a column is editable.
8443 * @param {Number} col The column index
8444 * @param {Boolean} editable True if the column is editable
8446 setEditable : function(col, editable){
8447 this.config[col].editable = editable;
8452 * Returns true if the column is hidden.
8453 * @param {Number} colIndex The column index
8456 isHidden : function(colIndex){
8457 return this.config[colIndex].hidden;
8462 * Returns true if the column width cannot be changed
8464 isFixed : function(colIndex){
8465 return this.config[colIndex].fixed;
8469 * Returns true if the column can be resized
8472 isResizable : function(colIndex){
8473 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8476 * Sets if a column is hidden.
8477 * @param {Number} colIndex The column index
8478 * @param {Boolean} hidden True if the column is hidden
8480 setHidden : function(colIndex, hidden){
8481 this.config[colIndex].hidden = hidden;
8482 this.totalWidth = null;
8483 this.fireEvent("hiddenchange", this, colIndex, hidden);
8487 * Sets the editor for a column.
8488 * @param {Number} col The column index
8489 * @param {Object} editor The editor object
8491 setEditor : function(col, editor){
8492 this.config[col].editor = editor;
8495 * Add a column (experimental...) - defaults to adding to the end..
8496 * @param {Object} config
8498 addColumn : function(c)
8501 var i = this.config.length;
8504 if(typeof c.dataIndex == "undefined"){
8507 if(typeof c.renderer == "string"){
8508 c.renderer = Roo.util.Format[c.renderer];
8510 if(typeof c.id == "undefined"){
8513 if(c.editor && c.editor.xtype){
8514 c.editor = Roo.factory(c.editor, Roo.grid);
8516 if(c.editor && c.editor.isFormField){
8517 c.editor = new Roo.grid.GridEditor(c.editor);
8519 this.lookup[c.id] = c;
8524 Roo.grid.ColumnModel.defaultRenderer = function(value)
8526 if(typeof value == "object") {
8529 if(typeof value == "string" && value.length < 1){
8533 return String.format("{0}", value);
8536 // Alias for backwards compatibility
8537 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8540 * Ext JS Library 1.1.1
8541 * Copyright(c) 2006-2007, Ext JS, LLC.
8543 * Originally Released Under LGPL - original licence link has changed is not relivant.
8546 * <script type="text/javascript">
8550 * @class Roo.LoadMask
8551 * A simple utility class for generically masking elements while loading data. If the element being masked has
8552 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8553 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8554 * element's UpdateManager load indicator and will be destroyed after the initial load.
8556 * Create a new LoadMask
8557 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8558 * @param {Object} config The config object
8560 Roo.LoadMask = function(el, config){
8561 this.el = Roo.get(el);
8562 Roo.apply(this, config);
8564 this.store.on('beforeload', this.onBeforeLoad, this);
8565 this.store.on('load', this.onLoad, this);
8566 this.store.on('loadexception', this.onLoadException, this);
8567 this.removeMask = false;
8569 var um = this.el.getUpdateManager();
8570 um.showLoadIndicator = false; // disable the default indicator
8571 um.on('beforeupdate', this.onBeforeLoad, this);
8572 um.on('update', this.onLoad, this);
8573 um.on('failure', this.onLoad, this);
8574 this.removeMask = true;
8578 Roo.LoadMask.prototype = {
8580 * @cfg {Boolean} removeMask
8581 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8582 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
8587 * The text to display in a centered loading message box (defaults to 'Loading...')
8591 * @cfg {String} msgCls
8592 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8594 msgCls : 'x-mask-loading',
8597 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8603 * Disables the mask to prevent it from being displayed
8605 disable : function(){
8606 this.disabled = true;
8610 * Enables the mask so that it can be displayed
8612 enable : function(){
8613 this.disabled = false;
8616 onLoadException : function()
8620 if (typeof(arguments[3]) != 'undefined') {
8621 Roo.MessageBox.alert("Error loading",arguments[3]);
8625 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8626 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8633 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8638 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8642 onBeforeLoad : function(){
8644 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8649 destroy : function(){
8651 this.store.un('beforeload', this.onBeforeLoad, this);
8652 this.store.un('load', this.onLoad, this);
8653 this.store.un('loadexception', this.onLoadException, this);
8655 var um = this.el.getUpdateManager();
8656 um.un('beforeupdate', this.onBeforeLoad, this);
8657 um.un('update', this.onLoad, this);
8658 um.un('failure', this.onLoad, this);
8662 * @class Roo.bootstrap.Table
8664 * @extends Roo.bootstrap.Component
8665 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
8666 * Similar to Roo.grid.Grid
8668 var table = Roo.factory({
8670 xns : Roo.bootstrap,
8671 autoSizeColumns: true,
8678 sortInfo : { direction : 'ASC', field: 'name' },
8680 xtype : 'HttpProxy',
8683 url : 'https://example.com/some.data.url.json'
8686 xtype : 'JsonReader',
8688 fields : [ 'id', 'name', whatever' ],
8695 xtype : 'ColumnModel',
8699 dataIndex : 'is_in_group',
8702 renderer : function(v, x , r) {
8704 return String.format("{0}", v)
8710 xtype : 'RowSelectionModel',
8711 xns : Roo.bootstrap.Table
8712 // you can add listeners to catch selection change here....
8718 grid.render(Roo.get("some-div"));
8721 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
8726 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
8727 * @cfg {Roo.data.Store} store The data store to use
8728 * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8730 * @cfg {String} cls table class
8733 * @cfg {boolean} striped Should the rows be alternative striped
8734 * @cfg {boolean} bordered Add borders to the table
8735 * @cfg {boolean} hover Add hover highlighting
8736 * @cfg {boolean} condensed Format condensed
8737 * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
8738 * also adds table-responsive (see bootstrap docs for details)
8739 * @cfg {Boolean} loadMask (true|false) default false
8740 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8741 * @cfg {Boolean} headerShow (true|false) generate thead, default true
8742 * @cfg {Boolean} rowSelection (true|false) default false
8743 * @cfg {Boolean} cellSelection (true|false) default false
8744 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8745 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
8746 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8747 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8748 * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8749 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
8752 * Create a new Table
8753 * @param {Object} config The config object
8756 Roo.bootstrap.Table = function(config)
8758 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8761 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8762 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8763 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8764 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8766 this.view = this; // compat with grid.
8768 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8770 this.sm.grid = this;
8771 this.selModel = Roo.factory(this.sm, Roo.grid);
8772 this.sm = this.selModel;
8773 this.sm.xmodule = this.xmodule || false;
8776 if (this.cm && typeof(this.cm.config) == 'undefined') {
8777 this.colModel = new Roo.grid.ColumnModel(this.cm);
8778 this.cm = this.colModel;
8779 this.cm.xmodule = this.xmodule || false;
8782 this.store= Roo.factory(this.store, Roo.data);
8783 this.ds = this.store;
8784 this.ds.xmodule = this.xmodule || false;
8787 if (this.footer && this.store) {
8788 this.footer.dataSource = this.ds;
8789 this.footer = Roo.factory(this.footer);
8796 * Fires when a cell is clicked
8797 * @param {Roo.bootstrap.Table} this
8798 * @param {Roo.Element} el
8799 * @param {Number} rowIndex
8800 * @param {Number} columnIndex
8801 * @param {Roo.EventObject} e
8805 * @event celldblclick
8806 * Fires when a cell is double clicked
8807 * @param {Roo.bootstrap.Table} this
8808 * @param {Roo.Element} el
8809 * @param {Number} rowIndex
8810 * @param {Number} columnIndex
8811 * @param {Roo.EventObject} e
8813 "celldblclick" : true,
8816 * Fires when a row is clicked
8817 * @param {Roo.bootstrap.Table} this
8818 * @param {Roo.Element} el
8819 * @param {Number} rowIndex
8820 * @param {Roo.EventObject} e
8824 * @event rowdblclick
8825 * Fires when a row is double clicked
8826 * @param {Roo.bootstrap.Table} this
8827 * @param {Roo.Element} el
8828 * @param {Number} rowIndex
8829 * @param {Roo.EventObject} e
8831 "rowdblclick" : true,
8834 * Fires when a mouseover occur
8835 * @param {Roo.bootstrap.Table} this
8836 * @param {Roo.Element} el
8837 * @param {Number} rowIndex
8838 * @param {Number} columnIndex
8839 * @param {Roo.EventObject} e
8844 * Fires when a mouseout occur
8845 * @param {Roo.bootstrap.Table} this
8846 * @param {Roo.Element} el
8847 * @param {Number} rowIndex
8848 * @param {Number} columnIndex
8849 * @param {Roo.EventObject} e
8854 * Fires when a row is rendered, so you can change add a style to it.
8855 * @param {Roo.bootstrap.Table} this
8856 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8860 * @event rowsrendered
8861 * Fires when all the rows have been rendered
8862 * @param {Roo.bootstrap.Table} this
8864 'rowsrendered' : true,
8866 * @event contextmenu
8867 * The raw contextmenu event for the entire grid.
8868 * @param {Roo.EventObject} e
8870 "contextmenu" : true,
8872 * @event rowcontextmenu
8873 * Fires when a row is right clicked
8874 * @param {Roo.bootstrap.Table} this
8875 * @param {Number} rowIndex
8876 * @param {Roo.EventObject} e
8878 "rowcontextmenu" : true,
8880 * @event cellcontextmenu
8881 * Fires when a cell is right clicked
8882 * @param {Roo.bootstrap.Table} this
8883 * @param {Number} rowIndex
8884 * @param {Number} cellIndex
8885 * @param {Roo.EventObject} e
8887 "cellcontextmenu" : true,
8889 * @event headercontextmenu
8890 * Fires when a header is right clicked
8891 * @param {Roo.bootstrap.Table} this
8892 * @param {Number} columnIndex
8893 * @param {Roo.EventObject} e
8895 "headercontextmenu" : true,
8898 * The raw mousedown event for the entire grid.
8899 * @param {Roo.EventObject} e
8906 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8922 enableColumnResize: true,
8924 rowSelection : false,
8925 cellSelection : false,
8928 minColumnWidth : 50,
8930 // Roo.Element - the tbody
8931 bodyEl: false, // <tbody> Roo.Element - thead element
8932 headEl: false, // <thead> Roo.Element - thead element
8933 resizeProxy : false, // proxy element for dragging?
8937 container: false, // used by gridpanel...
8943 auto_hide_footer : false,
8945 view: false, // actually points to this..
8947 getAutoCreate : function()
8949 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8956 // this get's auto added by panel.Grid
8957 if (this.scrollBody) {
8958 cfg.cls += ' table-body-fixed';
8961 cfg.cls += ' table-striped';
8965 cfg.cls += ' table-hover';
8967 if (this.bordered) {
8968 cfg.cls += ' table-bordered';
8970 if (this.condensed) {
8971 cfg.cls += ' table-condensed';
8974 if (this.responsive) {
8975 cfg.cls += ' table-responsive';
8979 cfg.cls+= ' ' +this.cls;
8985 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8988 if(this.store || this.cm){
8989 if(this.headerShow){
8990 cfg.cn.push(this.renderHeader());
8993 cfg.cn.push(this.renderBody());
8995 if(this.footerShow){
8996 cfg.cn.push(this.renderFooter());
8998 // where does this come from?
8999 //cfg.cls+= ' TableGrid';
9002 return { cn : [ cfg ] };
9005 initEvents : function()
9007 if(!this.store || !this.cm){
9010 if (this.selModel) {
9011 this.selModel.initEvents();
9015 //Roo.log('initEvents with ds!!!!');
9017 this.bodyEl = this.el.select('tbody', true).first();
9018 this.headEl = this.el.select('thead', true).first();
9019 this.mainFoot = this.el.select('tfoot', true).first();
9024 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9025 e.on('click', this.sort, this);
9029 // why is this done????? = it breaks dialogs??
9030 //this.parent().el.setStyle('position', 'relative');
9034 this.footer.parentId = this.id;
9035 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9038 this.el.select('tfoot tr td').first().addClass('hide');
9043 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9046 this.store.on('load', this.onLoad, this);
9047 this.store.on('beforeload', this.onBeforeLoad, this);
9048 this.store.on('update', this.onUpdate, this);
9049 this.store.on('add', this.onAdd, this);
9050 this.store.on("clear", this.clear, this);
9052 this.el.on("contextmenu", this.onContextMenu, this);
9055 this.cm.on("headerchange", this.onHeaderChange, this);
9056 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9058 //?? does bodyEl get replaced on render?
9059 this.bodyEl.on("click", this.onClick, this);
9060 this.bodyEl.on("dblclick", this.onDblClick, this);
9061 this.bodyEl.on('scroll', this.onBodyScroll, this);
9063 // guessing mainbody will work - this relays usually caught by selmodel at present.
9064 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9067 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9070 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9071 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9076 // Compatibility with grid - we implement all the view features at present.
9077 getView : function()
9082 initCSS : function()
9086 var cm = this.cm, styles = [];
9087 this.CSS.removeStyleSheet(this.id + '-cssrules');
9088 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9089 // we can honour xs/sm/md/xl as widths...
9090 // we first have to decide what widht we are currently at...
9091 var sz = Roo.getGridSize();
9095 var cols = []; // visable cols.
9097 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9098 var w = cm.getColumnWidth(i, false);
9100 cols.push( { rel : false, abs : 0 });
9104 cols.push( { rel : false, abs : w });
9106 last = i; // not really..
9109 var w = cm.getColumnWidth(i, sz);
9114 cols.push( { rel : w, abs : false });
9117 var avail = this.bodyEl.dom.clientWidth - total_abs;
9119 var unitWidth = Math.floor(avail / total);
9120 var rem = avail - (unitWidth * total);
9122 var hidden, width, pos = 0 , splithide , left;
9123 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9125 hidden = 'display:none;';
9127 width = 'width:0px;';
9129 if(!cm.isHidden(i)){
9133 // we can honour xs/sm/md/xl ?
9134 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9136 hidden = 'display:none;';
9138 // width should return a small number...
9140 w+=rem; // add the remaining with..
9143 left = "left:" + (pos -4) + "px;";
9144 width = "width:" + w+ "px;";
9147 if (this.responsive) {
9150 hidden = cm.isHidden(i) ? 'display:none;' : '';
9151 splithide = 'display: none;';
9154 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9157 splithide = 'display:none;';
9160 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9161 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9166 //Roo.log(styles.join(''));
9167 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9173 onContextMenu : function(e, t)
9175 this.processEvent("contextmenu", e);
9178 processEvent : function(name, e)
9180 if (name != 'touchstart' ) {
9181 this.fireEvent(name, e);
9184 var t = e.getTarget();
9186 var cell = Roo.get(t);
9192 if(cell.findParent('tfoot', false, true)){
9196 if(cell.findParent('thead', false, true)){
9198 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9199 cell = Roo.get(t).findParent('th', false, true);
9201 Roo.log("failed to find th in thead?");
9202 Roo.log(e.getTarget());
9207 var cellIndex = cell.dom.cellIndex;
9209 var ename = name == 'touchstart' ? 'click' : name;
9210 this.fireEvent("header" + ename, this, cellIndex, e);
9215 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9216 cell = Roo.get(t).findParent('td', false, true);
9218 Roo.log("failed to find th in tbody?");
9219 Roo.log(e.getTarget());
9224 var row = cell.findParent('tr', false, true);
9225 var cellIndex = cell.dom.cellIndex;
9226 var rowIndex = row.dom.rowIndex - 1;
9230 this.fireEvent("row" + name, this, rowIndex, e);
9234 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9240 onMouseover : function(e, el)
9242 var cell = Roo.get(el);
9248 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9249 cell = cell.findParent('td', false, true);
9252 var row = cell.findParent('tr', false, true);
9253 var cellIndex = cell.dom.cellIndex;
9254 var rowIndex = row.dom.rowIndex - 1; // start from 0
9256 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9260 onMouseout : function(e, el)
9262 var cell = Roo.get(el);
9268 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9269 cell = cell.findParent('td', false, true);
9272 var row = cell.findParent('tr', false, true);
9273 var cellIndex = cell.dom.cellIndex;
9274 var rowIndex = row.dom.rowIndex - 1; // start from 0
9276 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9280 onClick : function(e, el)
9282 var cell = Roo.get(el);
9284 if(!cell || (!this.cellSelection && !this.rowSelection)){
9288 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9289 cell = cell.findParent('td', false, true);
9292 if(!cell || typeof(cell) == 'undefined'){
9296 var row = cell.findParent('tr', false, true);
9298 if(!row || typeof(row) == 'undefined'){
9302 var cellIndex = cell.dom.cellIndex;
9303 var rowIndex = this.getRowIndex(row);
9305 // why??? - should these not be based on SelectionModel?
9306 //if(this.cellSelection){
9307 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9310 //if(this.rowSelection){
9311 this.fireEvent('rowclick', this, row, rowIndex, e);
9316 onDblClick : function(e,el)
9318 var cell = Roo.get(el);
9320 if(!cell || (!this.cellSelection && !this.rowSelection)){
9324 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9325 cell = cell.findParent('td', false, true);
9328 if(!cell || typeof(cell) == 'undefined'){
9332 var row = cell.findParent('tr', false, true);
9334 if(!row || typeof(row) == 'undefined'){
9338 var cellIndex = cell.dom.cellIndex;
9339 var rowIndex = this.getRowIndex(row);
9341 if(this.cellSelection){
9342 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9345 if(this.rowSelection){
9346 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9349 findRowIndex : function(el)
9351 var cell = Roo.get(el);
9355 var row = cell.findParent('tr', false, true);
9357 if(!row || typeof(row) == 'undefined'){
9360 return this.getRowIndex(row);
9362 sort : function(e,el)
9364 var col = Roo.get(el);
9366 if(!col.hasClass('sortable')){
9370 var sort = col.attr('sort');
9373 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9377 this.store.sortInfo = {field : sort, direction : dir};
9380 Roo.log("calling footer first");
9381 this.footer.onClick('first');
9384 this.store.load({ params : { start : 0 } });
9388 renderHeader : function()
9396 this.totalWidth = 0;
9398 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9400 var config = cm.config[i];
9404 cls : 'x-hcol-' + i,
9407 html: cm.getColumnHeader(i)
9410 var tooltip = cm.getColumnTooltip(i);
9412 c.tooltip = tooltip;
9418 if(typeof(config.sortable) != 'undefined' && config.sortable){
9419 c.cls += ' sortable';
9420 c.html = '<i class="fa"></i>' + c.html;
9423 // could use BS4 hidden-..-down
9425 if(typeof(config.lgHeader) != 'undefined'){
9426 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9429 if(typeof(config.mdHeader) != 'undefined'){
9430 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9433 if(typeof(config.smHeader) != 'undefined'){
9434 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9437 if(typeof(config.xsHeader) != 'undefined'){
9438 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9445 if(typeof(config.tooltip) != 'undefined'){
9446 c.tooltip = config.tooltip;
9449 if(typeof(config.colspan) != 'undefined'){
9450 c.colspan = config.colspan;
9453 // hidden is handled by CSS now
9455 if(typeof(config.dataIndex) != 'undefined'){
9456 c.sort = config.dataIndex;
9461 if(typeof(config.align) != 'undefined' && config.align.length){
9462 c.style += ' text-align:' + config.align + ';';
9465 /* width is done in CSS
9466 *if(typeof(config.width) != 'undefined'){
9467 c.style += ' width:' + config.width + 'px;';
9468 this.totalWidth += config.width;
9470 this.totalWidth += 100; // assume minimum of 100 per column?
9474 if(typeof(config.cls) != 'undefined'){
9475 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9477 // this is the bit that doesnt reall work at all...
9479 if (this.responsive) {
9482 ['xs','sm','md','lg'].map(function(size){
9484 if(typeof(config[size]) == 'undefined'){
9488 if (!config[size]) { // 0 = hidden
9489 // BS 4 '0' is treated as hide that column and below.
9490 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9494 c.cls += ' col-' + size + '-' + config[size] + (
9495 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9503 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9514 renderBody : function()
9524 colspan : this.cm.getColumnCount()
9534 renderFooter : function()
9544 colspan : this.cm.getColumnCount()
9558 // Roo.log('ds onload');
9563 var ds = this.store;
9565 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9566 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9567 if (_this.store.sortInfo) {
9569 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9570 e.select('i', true).addClass(['fa-arrow-up']);
9573 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9574 e.select('i', true).addClass(['fa-arrow-down']);
9579 var tbody = this.bodyEl;
9581 if(ds.getCount() > 0){
9582 ds.data.each(function(d,rowIndex){
9583 var row = this.renderRow(cm, ds, rowIndex);
9585 tbody.createChild(row);
9589 if(row.cellObjects.length){
9590 Roo.each(row.cellObjects, function(r){
9591 _this.renderCellObject(r);
9598 var tfoot = this.el.select('tfoot', true).first();
9600 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9602 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9604 var total = this.ds.getTotalCount();
9606 if(this.footer.pageSize < total){
9607 this.mainFoot.show();
9611 Roo.each(this.el.select('tbody td', true).elements, function(e){
9612 e.on('mouseover', _this.onMouseover, _this);
9615 Roo.each(this.el.select('tbody td', true).elements, function(e){
9616 e.on('mouseout', _this.onMouseout, _this);
9618 this.fireEvent('rowsrendered', this);
9622 this.initCSS(); /// resize cols
9628 onUpdate : function(ds,record)
9630 this.refreshRow(record);
9634 onRemove : function(ds, record, index, isUpdate){
9635 if(isUpdate !== true){
9636 this.fireEvent("beforerowremoved", this, index, record);
9638 var bt = this.bodyEl.dom;
9640 var rows = this.el.select('tbody > tr', true).elements;
9642 if(typeof(rows[index]) != 'undefined'){
9643 bt.removeChild(rows[index].dom);
9646 // if(bt.rows[index]){
9647 // bt.removeChild(bt.rows[index]);
9650 if(isUpdate !== true){
9651 //this.stripeRows(index);
9652 //this.syncRowHeights(index, index);
9654 this.fireEvent("rowremoved", this, index, record);
9658 onAdd : function(ds, records, rowIndex)
9660 //Roo.log('on Add called');
9661 // - note this does not handle multiple adding very well..
9662 var bt = this.bodyEl.dom;
9663 for (var i =0 ; i < records.length;i++) {
9664 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9665 //Roo.log(records[i]);
9666 //Roo.log(this.store.getAt(rowIndex+i));
9667 this.insertRow(this.store, rowIndex + i, false);
9674 refreshRow : function(record){
9675 var ds = this.store, index;
9676 if(typeof record == 'number'){
9678 record = ds.getAt(index);
9680 index = ds.indexOf(record);
9682 return; // should not happen - but seems to
9685 this.insertRow(ds, index, true);
9687 this.onRemove(ds, record, index+1, true);
9689 //this.syncRowHeights(index, index);
9691 this.fireEvent("rowupdated", this, index, record);
9693 // private - called by RowSelection
9694 onRowSelect : function(rowIndex){
9695 var row = this.getRowDom(rowIndex);
9696 row.addClass(['bg-info','info']);
9698 // private - called by RowSelection
9699 onRowDeselect : function(rowIndex)
9704 var row = this.getRowDom(rowIndex);
9705 row.removeClass(['bg-info','info']);
9708 * Focuses the specified row.
9709 * @param {Number} row The row index
9711 focusRow : function(row)
9713 //Roo.log('GridView.focusRow');
9714 var x = this.bodyEl.dom.scrollLeft;
9715 this.focusCell(row, 0, false);
9716 this.bodyEl.dom.scrollLeft = x;
9720 * Focuses the specified cell.
9721 * @param {Number} row The row index
9722 * @param {Number} col The column index
9723 * @param {Boolean} hscroll false to disable horizontal scrolling
9725 focusCell : function(row, col, hscroll)
9727 //Roo.log('GridView.focusCell');
9728 var el = this.ensureVisible(row, col, hscroll);
9729 // not sure what focusEL achives = it's a <a> pos relative
9730 //this.focusEl.alignTo(el, "tl-tl");
9732 // this.focusEl.focus();
9734 // this.focusEl.focus.defer(1, this.focusEl);
9739 * Scrolls the specified cell into view
9740 * @param {Number} row The row index
9741 * @param {Number} col The column index
9742 * @param {Boolean} hscroll false to disable horizontal scrolling
9744 ensureVisible : function(row, col, hscroll)
9746 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9747 //return null; //disable for testing.
9748 if(typeof row != "number"){
9751 if(row < 0 && row >= this.ds.getCount()){
9754 col = (col !== undefined ? col : 0);
9756 while(cm.isHidden(col)){
9760 var el = this.getCellDom(row, col);
9764 var c = this.bodyEl.dom;
9766 var ctop = parseInt(el.offsetTop, 10);
9767 var cleft = parseInt(el.offsetLeft, 10);
9768 var cbot = ctop + el.offsetHeight;
9769 var cright = cleft + el.offsetWidth;
9771 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9772 var ch = 0; //?? header is not withing the area?
9773 var stop = parseInt(c.scrollTop, 10);
9774 var sleft = parseInt(c.scrollLeft, 10);
9775 var sbot = stop + ch;
9776 var sright = sleft + c.clientWidth;
9778 Roo.log('GridView.ensureVisible:' +
9780 ' c.clientHeight:' + c.clientHeight +
9781 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9790 //Roo.log("set scrolltop to ctop DISABLE?");
9791 }else if(cbot > sbot){
9792 //Roo.log("set scrolltop to cbot-ch");
9793 c.scrollTop = cbot-ch;
9796 if(hscroll !== false){
9798 c.scrollLeft = cleft;
9799 }else if(cright > sright){
9800 c.scrollLeft = cright-c.clientWidth;
9808 insertRow : function(dm, rowIndex, isUpdate){
9811 this.fireEvent("beforerowsinserted", this, rowIndex);
9813 //var s = this.getScrollState();
9814 var row = this.renderRow(this.cm, this.store, rowIndex);
9815 // insert before rowIndex..
9816 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9820 if(row.cellObjects.length){
9821 Roo.each(row.cellObjects, function(r){
9822 _this.renderCellObject(r);
9827 this.fireEvent("rowsinserted", this, rowIndex);
9828 //this.syncRowHeights(firstRow, lastRow);
9829 //this.stripeRows(firstRow);
9836 getRowDom : function(rowIndex)
9838 var rows = this.el.select('tbody > tr', true).elements;
9840 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9843 getCellDom : function(rowIndex, colIndex)
9845 var row = this.getRowDom(rowIndex);
9846 if (row === false) {
9849 var cols = row.select('td', true).elements;
9850 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9854 // returns the object tree for a tr..
9857 renderRow : function(cm, ds, rowIndex)
9859 var d = ds.getAt(rowIndex);
9863 cls : 'x-row-' + rowIndex,
9867 var cellObjects = [];
9869 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9870 var config = cm.config[i];
9872 var renderer = cm.getRenderer(i);
9876 if(typeof(renderer) !== 'undefined'){
9877 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9879 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9880 // and are rendered into the cells after the row is rendered - using the id for the element.
9882 if(typeof(value) === 'object'){
9892 rowIndex : rowIndex,
9897 this.fireEvent('rowclass', this, rowcfg);
9901 // this might end up displaying HTML?
9902 // this is too messy... - better to only do it on columsn you know are going to be too long
9903 //tooltip : (typeof(value) === 'object') ? '' : value,
9904 cls : rowcfg.rowClass + ' x-col-' + i,
9906 html: (typeof(value) === 'object') ? '' : value
9913 if(typeof(config.colspan) != 'undefined'){
9914 td.colspan = config.colspan;
9919 if(typeof(config.align) != 'undefined' && config.align.length){
9920 td.style += ' text-align:' + config.align + ';';
9922 if(typeof(config.valign) != 'undefined' && config.valign.length){
9923 td.style += ' vertical-align:' + config.valign + ';';
9926 if(typeof(config.width) != 'undefined'){
9927 td.style += ' width:' + config.width + 'px;';
9931 if(typeof(config.cursor) != 'undefined'){
9932 td.style += ' cursor:' + config.cursor + ';';
9935 if(typeof(config.cls) != 'undefined'){
9936 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9938 if (this.responsive) {
9939 ['xs','sm','md','lg'].map(function(size){
9941 if(typeof(config[size]) == 'undefined'){
9947 if (!config[size]) { // 0 = hidden
9948 // BS 4 '0' is treated as hide that column and below.
9949 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9953 td.cls += ' col-' + size + '-' + config[size] + (
9954 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9964 row.cellObjects = cellObjects;
9972 onBeforeLoad : function()
9981 this.el.select('tbody', true).first().dom.innerHTML = '';
9984 * Show or hide a row.
9985 * @param {Number} rowIndex to show or hide
9986 * @param {Boolean} state hide
9988 setRowVisibility : function(rowIndex, state)
9990 var bt = this.bodyEl.dom;
9992 var rows = this.el.select('tbody > tr', true).elements;
9994 if(typeof(rows[rowIndex]) == 'undefined'){
9997 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10002 getSelectionModel : function(){
10003 if(!this.selModel){
10004 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10006 return this.selModel;
10009 * Render the Roo.bootstrap object from renderder
10011 renderCellObject : function(r)
10015 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10017 var t = r.cfg.render(r.container);
10020 Roo.each(r.cfg.cn, function(c){
10022 container: t.getChildContainer(),
10025 _this.renderCellObject(child);
10030 * get the Row Index from a dom element.
10031 * @param {Roo.Element} row The row to look for
10032 * @returns {Number} the row
10034 getRowIndex : function(row)
10038 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10049 * get the header TH element for columnIndex
10050 * @param {Number} columnIndex
10051 * @returns {Roo.Element}
10053 getHeaderIndex: function(colIndex)
10055 var cols = this.headEl.select('th', true).elements;
10056 return cols[colIndex];
10059 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10060 * @param {domElement} cell to look for
10061 * @returns {Number} the column
10063 getCellIndex : function(cell)
10065 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10067 return parseInt(id[1], 10);
10072 * Returns the grid's underlying element = used by panel.Grid
10073 * @return {Element} The element
10075 getGridEl : function(){
10079 * Forces a resize - used by panel.Grid
10080 * @return {Element} The element
10082 autoSize : function()
10084 //var ctr = Roo.get(this.container.dom.parentElement);
10085 var ctr = Roo.get(this.el.dom);
10087 var thd = this.getGridEl().select('thead',true).first();
10088 var tbd = this.getGridEl().select('tbody', true).first();
10089 var tfd = this.getGridEl().select('tfoot', true).first();
10091 var cw = ctr.getWidth();
10092 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10096 tbd.setWidth(ctr.getWidth());
10097 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10098 // this needs fixing for various usage - currently only hydra job advers I think..
10100 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10102 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10105 cw = Math.max(cw, this.totalWidth);
10106 this.getGridEl().select('tbody tr',true).setWidth(cw);
10109 // resize 'expandable coloumn?
10111 return; // we doe not have a view in this design..
10114 onBodyScroll: function()
10116 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10118 this.headEl.setStyle({
10119 'position' : 'relative',
10120 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10126 var scrollHeight = this.bodyEl.dom.scrollHeight;
10128 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10130 var height = this.bodyEl.getHeight();
10132 if(scrollHeight - height == scrollTop) {
10134 var total = this.ds.getTotalCount();
10136 if(this.footer.cursor + this.footer.pageSize < total){
10138 this.footer.ds.load({
10140 start : this.footer.cursor + this.footer.pageSize,
10141 limit : this.footer.pageSize
10150 onColumnSplitterMoved : function(i, diff)
10152 this.userResized = true;
10154 var cm = this.colModel;
10156 var w = this.getHeaderIndex(i).getWidth() + diff;
10159 cm.setColumnWidth(i, w, true);
10161 //var cid = cm.getColumnId(i); << not used in this version?
10162 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10164 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10165 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10166 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10168 //this.updateSplitters();
10169 //this.layout(); << ??
10170 this.fireEvent("columnresize", i, w);
10172 onHeaderChange : function()
10174 var header = this.renderHeader();
10175 var table = this.el.select('table', true).first();
10177 this.headEl.remove();
10178 this.headEl = table.createChild(header, this.bodyEl, false);
10180 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10181 e.on('click', this.sort, this);
10184 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10185 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10190 onHiddenChange : function(colModel, colIndex, hidden)
10193 this.cm.setHidden()
10194 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10195 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10197 this.CSS.updateRule(thSelector, "display", "");
10198 this.CSS.updateRule(tdSelector, "display", "");
10201 this.CSS.updateRule(thSelector, "display", "none");
10202 this.CSS.updateRule(tdSelector, "display", "none");
10205 // onload calls initCSS()
10206 this.onHeaderChange();
10210 setColumnWidth: function(col_index, width)
10212 // width = "md-2 xs-2..."
10213 if(!this.colModel.config[col_index]) {
10217 var w = width.split(" ");
10219 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10221 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10224 for(var j = 0; j < w.length; j++) {
10230 var size_cls = w[j].split("-");
10232 if(!Number.isInteger(size_cls[1] * 1)) {
10236 if(!this.colModel.config[col_index][size_cls[0]]) {
10240 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10244 h_row[0].classList.replace(
10245 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10246 "col-"+size_cls[0]+"-"+size_cls[1]
10249 for(var i = 0; i < rows.length; i++) {
10251 var size_cls = w[j].split("-");
10253 if(!Number.isInteger(size_cls[1] * 1)) {
10257 if(!this.colModel.config[col_index][size_cls[0]]) {
10261 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10265 rows[i].classList.replace(
10266 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10267 "col-"+size_cls[0]+"-"+size_cls[1]
10271 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10276 // currently only used to find the split on drag..
10277 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10282 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10283 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10292 * @class Roo.bootstrap.TableCell
10293 * @extends Roo.bootstrap.Component
10294 * Bootstrap TableCell class
10295 * @cfg {String} html cell contain text
10296 * @cfg {String} cls cell class
10297 * @cfg {String} tag cell tag (td|th) default td
10298 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10299 * @cfg {String} align Aligns the content in a cell
10300 * @cfg {String} axis Categorizes cells
10301 * @cfg {String} bgcolor Specifies the background color of a cell
10302 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10303 * @cfg {Number} colspan Specifies the number of columns a cell should span
10304 * @cfg {String} headers Specifies one or more header cells a cell is related to
10305 * @cfg {Number} height Sets the height of a cell
10306 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10307 * @cfg {Number} rowspan Sets the number of rows a cell should span
10308 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10309 * @cfg {String} valign Vertical aligns the content in a cell
10310 * @cfg {Number} width Specifies the width of a cell
10313 * Create a new TableCell
10314 * @param {Object} config The config object
10317 Roo.bootstrap.TableCell = function(config){
10318 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10321 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10341 getAutoCreate : function(){
10342 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10349 cfg.tag = this.tag;
10362 cfg.align=this.align
10367 if (this.bgcolor) {
10368 cfg.bgcolor=this.bgcolor
10370 if (this.charoff) {
10371 cfg.charoff=this.charoff
10373 if (this.colspan) {
10374 cfg.colspan=this.colspan
10376 if (this.headers) {
10377 cfg.headers=this.headers
10380 cfg.height=this.height
10383 cfg.nowrap=this.nowrap
10385 if (this.rowspan) {
10386 cfg.rowspan=this.rowspan
10389 cfg.scope=this.scope
10392 cfg.valign=this.valign
10395 cfg.width=this.width
10414 * @class Roo.bootstrap.TableRow
10415 * @extends Roo.bootstrap.Component
10416 * Bootstrap TableRow class
10417 * @cfg {String} cls row class
10418 * @cfg {String} align Aligns the content in a table row
10419 * @cfg {String} bgcolor Specifies a background color for a table row
10420 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10421 * @cfg {String} valign Vertical aligns the content in a table row
10424 * Create a new TableRow
10425 * @param {Object} config The config object
10428 Roo.bootstrap.TableRow = function(config){
10429 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10432 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10440 getAutoCreate : function(){
10441 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10448 cfg.cls = this.cls;
10451 cfg.align = this.align;
10454 cfg.bgcolor = this.bgcolor;
10457 cfg.charoff = this.charoff;
10460 cfg.valign = this.valign;
10478 * @class Roo.bootstrap.TableBody
10479 * @extends Roo.bootstrap.Component
10480 * Bootstrap TableBody class
10481 * @cfg {String} cls element class
10482 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10483 * @cfg {String} align Aligns the content inside the element
10484 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10485 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10488 * Create a new TableBody
10489 * @param {Object} config The config object
10492 Roo.bootstrap.TableBody = function(config){
10493 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10496 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10504 getAutoCreate : function(){
10505 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10515 cfg.tag = this.tag;
10519 cfg.align = this.align;
10522 cfg.charoff = this.charoff;
10525 cfg.valign = this.valign;
10532 // initEvents : function()
10535 // if(!this.store){
10539 // this.store = Roo.factory(this.store, Roo.data);
10540 // this.store.on('load', this.onLoad, this);
10542 // this.store.load();
10546 // onLoad: function ()
10548 // this.fireEvent('load', this);
10558 * Ext JS Library 1.1.1
10559 * Copyright(c) 2006-2007, Ext JS, LLC.
10561 * Originally Released Under LGPL - original licence link has changed is not relivant.
10564 * <script type="text/javascript">
10567 // as we use this in bootstrap.
10568 Roo.namespace('Roo.form');
10570 * @class Roo.form.Action
10571 * Internal Class used to handle form actions
10573 * @param {Roo.form.BasicForm} el The form element or its id
10574 * @param {Object} config Configuration options
10579 // define the action interface
10580 Roo.form.Action = function(form, options){
10582 this.options = options || {};
10585 * Client Validation Failed
10588 Roo.form.Action.CLIENT_INVALID = 'client';
10590 * Server Validation Failed
10593 Roo.form.Action.SERVER_INVALID = 'server';
10595 * Connect to Server Failed
10598 Roo.form.Action.CONNECT_FAILURE = 'connect';
10600 * Reading Data from Server Failed
10603 Roo.form.Action.LOAD_FAILURE = 'load';
10605 Roo.form.Action.prototype = {
10607 failureType : undefined,
10608 response : undefined,
10609 result : undefined,
10611 // interface method
10612 run : function(options){
10616 // interface method
10617 success : function(response){
10621 // interface method
10622 handleResponse : function(response){
10626 // default connection failure
10627 failure : function(response){
10629 this.response = response;
10630 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10631 this.form.afterAction(this, false);
10634 processResponse : function(response){
10635 this.response = response;
10636 if(!response.responseText){
10639 this.result = this.handleResponse(response);
10640 return this.result;
10643 // utility functions used internally
10644 getUrl : function(appendParams){
10645 var url = this.options.url || this.form.url || this.form.el.dom.action;
10647 var p = this.getParams();
10649 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10655 getMethod : function(){
10656 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10659 getParams : function(){
10660 var bp = this.form.baseParams;
10661 var p = this.options.params;
10663 if(typeof p == "object"){
10664 p = Roo.urlEncode(Roo.applyIf(p, bp));
10665 }else if(typeof p == 'string' && bp){
10666 p += '&' + Roo.urlEncode(bp);
10669 p = Roo.urlEncode(bp);
10674 createCallback : function(){
10676 success: this.success,
10677 failure: this.failure,
10679 timeout: (this.form.timeout*1000),
10680 upload: this.form.fileUpload ? this.success : undefined
10685 Roo.form.Action.Submit = function(form, options){
10686 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10689 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10692 haveProgress : false,
10693 uploadComplete : false,
10695 // uploadProgress indicator.
10696 uploadProgress : function()
10698 if (!this.form.progressUrl) {
10702 if (!this.haveProgress) {
10703 Roo.MessageBox.progress("Uploading", "Uploading");
10705 if (this.uploadComplete) {
10706 Roo.MessageBox.hide();
10710 this.haveProgress = true;
10712 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10714 var c = new Roo.data.Connection();
10716 url : this.form.progressUrl,
10721 success : function(req){
10722 //console.log(data);
10726 rdata = Roo.decode(req.responseText)
10728 Roo.log("Invalid data from server..");
10732 if (!rdata || !rdata.success) {
10734 Roo.MessageBox.alert(Roo.encode(rdata));
10737 var data = rdata.data;
10739 if (this.uploadComplete) {
10740 Roo.MessageBox.hide();
10745 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10746 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10749 this.uploadProgress.defer(2000,this);
10752 failure: function(data) {
10753 Roo.log('progress url failed ');
10764 // run get Values on the form, so it syncs any secondary forms.
10765 this.form.getValues();
10767 var o = this.options;
10768 var method = this.getMethod();
10769 var isPost = method == 'POST';
10770 if(o.clientValidation === false || this.form.isValid()){
10772 if (this.form.progressUrl) {
10773 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10774 (new Date() * 1) + '' + Math.random());
10779 Roo.Ajax.request(Roo.apply(this.createCallback(), {
10780 form:this.form.el.dom,
10781 url:this.getUrl(!isPost),
10783 params:isPost ? this.getParams() : null,
10784 isUpload: this.form.fileUpload,
10785 formData : this.form.formData
10788 this.uploadProgress();
10790 }else if (o.clientValidation !== false){ // client validation failed
10791 this.failureType = Roo.form.Action.CLIENT_INVALID;
10792 this.form.afterAction(this, false);
10796 success : function(response)
10798 this.uploadComplete= true;
10799 if (this.haveProgress) {
10800 Roo.MessageBox.hide();
10804 var result = this.processResponse(response);
10805 if(result === true || result.success){
10806 this.form.afterAction(this, true);
10810 this.form.markInvalid(result.errors);
10811 this.failureType = Roo.form.Action.SERVER_INVALID;
10813 this.form.afterAction(this, false);
10815 failure : function(response)
10817 this.uploadComplete= true;
10818 if (this.haveProgress) {
10819 Roo.MessageBox.hide();
10822 this.response = response;
10823 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10824 this.form.afterAction(this, false);
10827 handleResponse : function(response){
10828 if(this.form.errorReader){
10829 var rs = this.form.errorReader.read(response);
10832 for(var i = 0, len = rs.records.length; i < len; i++) {
10833 var r = rs.records[i];
10834 errors[i] = r.data;
10837 if(errors.length < 1){
10841 success : rs.success,
10847 ret = Roo.decode(response.responseText);
10851 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10861 Roo.form.Action.Load = function(form, options){
10862 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10863 this.reader = this.form.reader;
10866 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10871 Roo.Ajax.request(Roo.apply(
10872 this.createCallback(), {
10873 method:this.getMethod(),
10874 url:this.getUrl(false),
10875 params:this.getParams()
10879 success : function(response){
10881 var result = this.processResponse(response);
10882 if(result === true || !result.success || !result.data){
10883 this.failureType = Roo.form.Action.LOAD_FAILURE;
10884 this.form.afterAction(this, false);
10887 this.form.clearInvalid();
10888 this.form.setValues(result.data);
10889 this.form.afterAction(this, true);
10892 handleResponse : function(response){
10893 if(this.form.reader){
10894 var rs = this.form.reader.read(response);
10895 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10897 success : rs.success,
10901 return Roo.decode(response.responseText);
10905 Roo.form.Action.ACTION_TYPES = {
10906 'load' : Roo.form.Action.Load,
10907 'submit' : Roo.form.Action.Submit
10916 * @class Roo.bootstrap.Form
10917 * @extends Roo.bootstrap.Component
10918 * Bootstrap Form class
10919 * @cfg {String} method GET | POST (default POST)
10920 * @cfg {String} labelAlign top | left (default top)
10921 * @cfg {String} align left | right - for navbars
10922 * @cfg {Boolean} loadMask load mask when submit (default true)
10926 * Create a new Form
10927 * @param {Object} config The config object
10931 Roo.bootstrap.Form = function(config){
10933 Roo.bootstrap.Form.superclass.constructor.call(this, config);
10935 Roo.bootstrap.Form.popover.apply();
10939 * @event clientvalidation
10940 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10941 * @param {Form} this
10942 * @param {Boolean} valid true if the form has passed client-side validation
10944 clientvalidation: true,
10946 * @event beforeaction
10947 * Fires before any action is performed. Return false to cancel the action.
10948 * @param {Form} this
10949 * @param {Action} action The action to be performed
10951 beforeaction: true,
10953 * @event actionfailed
10954 * Fires when an action fails.
10955 * @param {Form} this
10956 * @param {Action} action The action that failed
10958 actionfailed : true,
10960 * @event actioncomplete
10961 * Fires when an action is completed.
10962 * @param {Form} this
10963 * @param {Action} action The action that completed
10965 actioncomplete : true
10969 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
10972 * @cfg {String} method
10973 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10977 * @cfg {String} url
10978 * The URL to use for form actions if one isn't supplied in the action options.
10981 * @cfg {Boolean} fileUpload
10982 * Set to true if this form is a file upload.
10986 * @cfg {Object} baseParams
10987 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10991 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10995 * @cfg {Sting} align (left|right) for navbar forms
11000 activeAction : null,
11003 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11004 * element by passing it or its id or mask the form itself by passing in true.
11007 waitMsgTarget : false,
11012 * @cfg {Boolean} errorMask (true|false) default false
11017 * @cfg {Number} maskOffset Default 100
11022 * @cfg {Boolean} maskBody
11026 getAutoCreate : function(){
11030 method : this.method || 'POST',
11031 id : this.id || Roo.id(),
11034 if (this.parent().xtype.match(/^Nav/)) {
11035 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11039 if (this.labelAlign == 'left' ) {
11040 cfg.cls += ' form-horizontal';
11046 initEvents : function()
11048 this.el.on('submit', this.onSubmit, this);
11049 // this was added as random key presses on the form where triggering form submit.
11050 this.el.on('keypress', function(e) {
11051 if (e.getCharCode() != 13) {
11054 // we might need to allow it for textareas.. and some other items.
11055 // check e.getTarget().
11057 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11061 Roo.log("keypress blocked");
11063 e.preventDefault();
11069 onSubmit : function(e){
11074 * Returns true if client-side validation on the form is successful.
11077 isValid : function(){
11078 var items = this.getItems();
11080 var target = false;
11082 items.each(function(f){
11088 Roo.log('invalid field: ' + f.name);
11092 if(!target && f.el.isVisible(true)){
11098 if(this.errorMask && !valid){
11099 Roo.bootstrap.Form.popover.mask(this, target);
11106 * Returns true if any fields in this form have changed since their original load.
11109 isDirty : function(){
11111 var items = this.getItems();
11112 items.each(function(f){
11122 * Performs a predefined action (submit or load) or custom actions you define on this form.
11123 * @param {String} actionName The name of the action type
11124 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11125 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11126 * accept other config options):
11128 Property Type Description
11129 ---------------- --------------- ----------------------------------------------------------------------------------
11130 url String The url for the action (defaults to the form's url)
11131 method String The form method to use (defaults to the form's method, or POST if not defined)
11132 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11133 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11134 validate the form on the client (defaults to false)
11136 * @return {BasicForm} this
11138 doAction : function(action, options){
11139 if(typeof action == 'string'){
11140 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11142 if(this.fireEvent('beforeaction', this, action) !== false){
11143 this.beforeAction(action);
11144 action.run.defer(100, action);
11150 beforeAction : function(action){
11151 var o = action.options;
11156 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11158 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11161 // not really supported yet.. ??
11163 //if(this.waitMsgTarget === true){
11164 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11165 //}else if(this.waitMsgTarget){
11166 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11167 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11169 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11175 afterAction : function(action, success){
11176 this.activeAction = null;
11177 var o = action.options;
11182 Roo.get(document.body).unmask();
11188 //if(this.waitMsgTarget === true){
11189 // this.el.unmask();
11190 //}else if(this.waitMsgTarget){
11191 // this.waitMsgTarget.unmask();
11193 // Roo.MessageBox.updateProgress(1);
11194 // Roo.MessageBox.hide();
11201 Roo.callback(o.success, o.scope, [this, action]);
11202 this.fireEvent('actioncomplete', this, action);
11206 // failure condition..
11207 // we have a scenario where updates need confirming.
11208 // eg. if a locking scenario exists..
11209 // we look for { errors : { needs_confirm : true }} in the response.
11211 (typeof(action.result) != 'undefined') &&
11212 (typeof(action.result.errors) != 'undefined') &&
11213 (typeof(action.result.errors.needs_confirm) != 'undefined')
11216 Roo.log("not supported yet");
11219 Roo.MessageBox.confirm(
11220 "Change requires confirmation",
11221 action.result.errorMsg,
11226 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11236 Roo.callback(o.failure, o.scope, [this, action]);
11237 // show an error message if no failed handler is set..
11238 if (!this.hasListener('actionfailed')) {
11239 Roo.log("need to add dialog support");
11241 Roo.MessageBox.alert("Error",
11242 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11243 action.result.errorMsg :
11244 "Saving Failed, please check your entries or try again"
11249 this.fireEvent('actionfailed', this, action);
11254 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11255 * @param {String} id The value to search for
11258 findField : function(id){
11259 var items = this.getItems();
11260 var field = items.get(id);
11262 items.each(function(f){
11263 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11270 return field || null;
11273 * Mark fields in this form invalid in bulk.
11274 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11275 * @return {BasicForm} this
11277 markInvalid : function(errors){
11278 if(errors instanceof Array){
11279 for(var i = 0, len = errors.length; i < len; i++){
11280 var fieldError = errors[i];
11281 var f = this.findField(fieldError.id);
11283 f.markInvalid(fieldError.msg);
11289 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11290 field.markInvalid(errors[id]);
11294 //Roo.each(this.childForms || [], function (f) {
11295 // f.markInvalid(errors);
11302 * Set values for fields in this form in bulk.
11303 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11304 * @return {BasicForm} this
11306 setValues : function(values){
11307 if(values instanceof Array){ // array of objects
11308 for(var i = 0, len = values.length; i < len; i++){
11310 var f = this.findField(v.id);
11312 f.setValue(v.value);
11313 if(this.trackResetOnLoad){
11314 f.originalValue = f.getValue();
11318 }else{ // object hash
11321 if(typeof values[id] != 'function' && (field = this.findField(id))){
11323 if (field.setFromData &&
11324 field.valueField &&
11325 field.displayField &&
11326 // combos' with local stores can
11327 // be queried via setValue()
11328 // to set their value..
11329 (field.store && !field.store.isLocal)
11333 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11334 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11335 field.setFromData(sd);
11337 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11339 field.setFromData(values);
11342 field.setValue(values[id]);
11346 if(this.trackResetOnLoad){
11347 field.originalValue = field.getValue();
11353 //Roo.each(this.childForms || [], function (f) {
11354 // f.setValues(values);
11361 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11362 * they are returned as an array.
11363 * @param {Boolean} asString
11366 getValues : function(asString){
11367 //if (this.childForms) {
11368 // copy values from the child forms
11369 // Roo.each(this.childForms, function (f) {
11370 // this.setValues(f.getValues());
11376 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11377 if(asString === true){
11380 return Roo.urlDecode(fs);
11384 * Returns the fields in this form as an object with key/value pairs.
11385 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11388 getFieldValues : function(with_hidden)
11390 var items = this.getItems();
11392 items.each(function(f){
11394 if (!f.getName()) {
11398 var v = f.getValue();
11400 if (f.inputType =='radio') {
11401 if (typeof(ret[f.getName()]) == 'undefined') {
11402 ret[f.getName()] = ''; // empty..
11405 if (!f.el.dom.checked) {
11409 v = f.el.dom.value;
11413 if(f.xtype == 'MoneyField'){
11414 ret[f.currencyName] = f.getCurrency();
11417 // not sure if this supported any more..
11418 if ((typeof(v) == 'object') && f.getRawValue) {
11419 v = f.getRawValue() ; // dates..
11421 // combo boxes where name != hiddenName...
11422 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11423 ret[f.name] = f.getRawValue();
11425 ret[f.getName()] = v;
11432 * Clears all invalid messages in this form.
11433 * @return {BasicForm} this
11435 clearInvalid : function(){
11436 var items = this.getItems();
11438 items.each(function(f){
11446 * Resets this form.
11447 * @return {BasicForm} this
11449 reset : function(){
11450 var items = this.getItems();
11451 items.each(function(f){
11455 Roo.each(this.childForms || [], function (f) {
11463 getItems : function()
11465 var r=new Roo.util.MixedCollection(false, function(o){
11466 return o.id || (o.id = Roo.id());
11468 var iter = function(el) {
11475 Roo.each(el.items,function(e) {
11484 hideFields : function(items)
11486 Roo.each(items, function(i){
11488 var f = this.findField(i);
11499 showFields : function(items)
11501 Roo.each(items, function(i){
11503 var f = this.findField(i);
11516 Roo.apply(Roo.bootstrap.Form, {
11532 intervalID : false,
11538 if(this.isApplied){
11543 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11544 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11545 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11546 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11549 this.maskEl.top.enableDisplayMode("block");
11550 this.maskEl.left.enableDisplayMode("block");
11551 this.maskEl.bottom.enableDisplayMode("block");
11552 this.maskEl.right.enableDisplayMode("block");
11554 this.toolTip = new Roo.bootstrap.Tooltip({
11555 cls : 'roo-form-error-popover',
11557 'left' : ['r-l', [-2,0], 'right'],
11558 'right' : ['l-r', [2,0], 'left'],
11559 'bottom' : ['tl-bl', [0,2], 'top'],
11560 'top' : [ 'bl-tl', [0,-2], 'bottom']
11564 this.toolTip.render(Roo.get(document.body));
11566 this.toolTip.el.enableDisplayMode("block");
11568 Roo.get(document.body).on('click', function(){
11572 Roo.get(document.body).on('touchstart', function(){
11576 this.isApplied = true
11579 mask : function(form, target)
11583 this.target = target;
11585 if(!this.form.errorMask || !target.el){
11589 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11591 Roo.log(scrollable);
11593 var ot = this.target.el.calcOffsetsTo(scrollable);
11595 var scrollTo = ot[1] - this.form.maskOffset;
11597 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11599 scrollable.scrollTo('top', scrollTo);
11601 var box = this.target.el.getBox();
11603 var zIndex = Roo.bootstrap.Modal.zIndex++;
11606 this.maskEl.top.setStyle('position', 'absolute');
11607 this.maskEl.top.setStyle('z-index', zIndex);
11608 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11609 this.maskEl.top.setLeft(0);
11610 this.maskEl.top.setTop(0);
11611 this.maskEl.top.show();
11613 this.maskEl.left.setStyle('position', 'absolute');
11614 this.maskEl.left.setStyle('z-index', zIndex);
11615 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11616 this.maskEl.left.setLeft(0);
11617 this.maskEl.left.setTop(box.y - this.padding);
11618 this.maskEl.left.show();
11620 this.maskEl.bottom.setStyle('position', 'absolute');
11621 this.maskEl.bottom.setStyle('z-index', zIndex);
11622 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11623 this.maskEl.bottom.setLeft(0);
11624 this.maskEl.bottom.setTop(box.bottom + this.padding);
11625 this.maskEl.bottom.show();
11627 this.maskEl.right.setStyle('position', 'absolute');
11628 this.maskEl.right.setStyle('z-index', zIndex);
11629 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11630 this.maskEl.right.setLeft(box.right + this.padding);
11631 this.maskEl.right.setTop(box.y - this.padding);
11632 this.maskEl.right.show();
11634 this.toolTip.bindEl = this.target.el;
11636 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11638 var tip = this.target.blankText;
11640 if(this.target.getValue() !== '' ) {
11642 if (this.target.invalidText.length) {
11643 tip = this.target.invalidText;
11644 } else if (this.target.regexText.length){
11645 tip = this.target.regexText;
11649 this.toolTip.show(tip);
11651 this.intervalID = window.setInterval(function() {
11652 Roo.bootstrap.Form.popover.unmask();
11655 window.onwheel = function(){ return false;};
11657 (function(){ this.isMasked = true; }).defer(500, this);
11661 unmask : function()
11663 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11667 this.maskEl.top.setStyle('position', 'absolute');
11668 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11669 this.maskEl.top.hide();
11671 this.maskEl.left.setStyle('position', 'absolute');
11672 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11673 this.maskEl.left.hide();
11675 this.maskEl.bottom.setStyle('position', 'absolute');
11676 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11677 this.maskEl.bottom.hide();
11679 this.maskEl.right.setStyle('position', 'absolute');
11680 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11681 this.maskEl.right.hide();
11683 this.toolTip.hide();
11685 this.toolTip.el.hide();
11687 window.onwheel = function(){ return true;};
11689 if(this.intervalID){
11690 window.clearInterval(this.intervalID);
11691 this.intervalID = false;
11694 this.isMasked = false;
11704 * Ext JS Library 1.1.1
11705 * Copyright(c) 2006-2007, Ext JS, LLC.
11707 * Originally Released Under LGPL - original licence link has changed is not relivant.
11710 * <script type="text/javascript">
11713 * @class Roo.form.VTypes
11714 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11717 Roo.form.VTypes = function(){
11718 // closure these in so they are only created once.
11719 var alpha = /^[a-zA-Z_]+$/;
11720 var alphanum = /^[a-zA-Z0-9_]+$/;
11721 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11722 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11724 // All these messages and functions are configurable
11727 * The function used to validate email addresses
11728 * @param {String} value The email address
11730 'email' : function(v){
11731 return email.test(v);
11734 * The error text to display when the email validation function returns false
11737 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11739 * The keystroke filter mask to be applied on email input
11742 'emailMask' : /[a-z0-9_\.\-@]/i,
11745 * The function used to validate URLs
11746 * @param {String} value The URL
11748 'url' : function(v){
11749 return url.test(v);
11752 * The error text to display when the url validation function returns false
11755 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11758 * The function used to validate alpha values
11759 * @param {String} value The value
11761 'alpha' : function(v){
11762 return alpha.test(v);
11765 * The error text to display when the alpha validation function returns false
11768 'alphaText' : 'This field should only contain letters and _',
11770 * The keystroke filter mask to be applied on alpha input
11773 'alphaMask' : /[a-z_]/i,
11776 * The function used to validate alphanumeric values
11777 * @param {String} value The value
11779 'alphanum' : function(v){
11780 return alphanum.test(v);
11783 * The error text to display when the alphanumeric validation function returns false
11786 'alphanumText' : 'This field should only contain letters, numbers and _',
11788 * The keystroke filter mask to be applied on alphanumeric input
11791 'alphanumMask' : /[a-z0-9_]/i
11801 * @class Roo.bootstrap.Input
11802 * @extends Roo.bootstrap.Component
11803 * Bootstrap Input class
11804 * @cfg {Boolean} disabled is it disabled
11805 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
11806 * @cfg {String} name name of the input
11807 * @cfg {string} fieldLabel - the label associated
11808 * @cfg {string} placeholder - placeholder to put in text.
11809 * @cfg {string} before - input group add on before
11810 * @cfg {string} after - input group add on after
11811 * @cfg {string} size - (lg|sm) or leave empty..
11812 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11813 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11814 * @cfg {Number} md colspan out of 12 for computer-sized screens
11815 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11816 * @cfg {string} value default value of the input
11817 * @cfg {Number} labelWidth set the width of label
11818 * @cfg {Number} labellg set the width of label (1-12)
11819 * @cfg {Number} labelmd set the width of label (1-12)
11820 * @cfg {Number} labelsm set the width of label (1-12)
11821 * @cfg {Number} labelxs set the width of label (1-12)
11822 * @cfg {String} labelAlign (top|left)
11823 * @cfg {Boolean} readOnly Specifies that the field should be read-only
11824 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11825 * @cfg {String} indicatorpos (left|right) default left
11826 * @cfg {String} capture (user|camera) use for file input only. (default empty)
11827 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11828 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11830 * @cfg {String} align (left|center|right) Default left
11831 * @cfg {Boolean} forceFeedback (true|false) Default false
11834 * Create a new Input
11835 * @param {Object} config The config object
11838 Roo.bootstrap.Input = function(config){
11840 Roo.bootstrap.Input.superclass.constructor.call(this, config);
11845 * Fires when this field receives input focus.
11846 * @param {Roo.form.Field} this
11851 * Fires when this field loses input focus.
11852 * @param {Roo.form.Field} this
11856 * @event specialkey
11857 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
11858 * {@link Roo.EventObject#getKey} to determine which key was pressed.
11859 * @param {Roo.form.Field} this
11860 * @param {Roo.EventObject} e The event object
11865 * Fires just before the field blurs if the field value has changed.
11866 * @param {Roo.form.Field} this
11867 * @param {Mixed} newValue The new value
11868 * @param {Mixed} oldValue The original value
11873 * Fires after the field has been marked as invalid.
11874 * @param {Roo.form.Field} this
11875 * @param {String} msg The validation message
11880 * Fires after the field has been validated with no errors.
11881 * @param {Roo.form.Field} this
11886 * Fires after the key up
11887 * @param {Roo.form.Field} this
11888 * @param {Roo.EventObject} e The event Object
11893 * Fires after the user pastes into input
11894 * @param {Roo.form.Field} this
11895 * @param {Roo.EventObject} e The event Object
11901 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
11903 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11904 automatic validation (defaults to "keyup").
11906 validationEvent : "keyup",
11908 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11910 validateOnBlur : true,
11912 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11914 validationDelay : 250,
11916 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11918 focusClass : "x-form-focus", // not needed???
11922 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11924 invalidClass : "has-warning",
11927 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11929 validClass : "has-success",
11932 * @cfg {Boolean} hasFeedback (true|false) default true
11934 hasFeedback : true,
11937 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11939 invalidFeedbackClass : "glyphicon-warning-sign",
11942 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11944 validFeedbackClass : "glyphicon-ok",
11947 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11949 selectOnFocus : false,
11952 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11956 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11961 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11963 disableKeyFilter : false,
11966 * @cfg {Boolean} disabled True to disable the field (defaults to false).
11970 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11974 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11976 blankText : "Please complete this mandatory field",
11979 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11983 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11985 maxLength : Number.MAX_VALUE,
11987 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11989 minLengthText : "The minimum length for this field is {0}",
11991 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11993 maxLengthText : "The maximum length for this field is {0}",
11997 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11998 * If available, this function will be called only after the basic validators all return true, and will be passed the
11999 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12003 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12004 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12005 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
12009 * @cfg {String} regexText -- Depricated - use Invalid Text
12014 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12020 autocomplete: false,
12024 inputType : 'text',
12027 placeholder: false,
12032 preventMark: false,
12033 isFormField : true,
12036 labelAlign : false,
12039 formatedValue : false,
12040 forceFeedback : false,
12042 indicatorpos : 'left',
12052 parentLabelAlign : function()
12055 while (parent.parent()) {
12056 parent = parent.parent();
12057 if (typeof(parent.labelAlign) !='undefined') {
12058 return parent.labelAlign;
12065 getAutoCreate : function()
12067 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12073 if(this.inputType != 'hidden'){
12074 cfg.cls = 'form-group' //input-group
12080 type : this.inputType,
12081 value : this.value,
12082 cls : 'form-control',
12083 placeholder : this.placeholder || '',
12084 autocomplete : this.autocomplete || 'new-password'
12086 if (this.inputType == 'file') {
12087 input.style = 'overflow:hidden'; // why not in CSS?
12090 if(this.capture.length){
12091 input.capture = this.capture;
12094 if(this.accept.length){
12095 input.accept = this.accept + "/*";
12099 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12102 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12103 input.maxLength = this.maxLength;
12106 if (this.disabled) {
12107 input.disabled=true;
12110 if (this.readOnly) {
12111 input.readonly=true;
12115 input.name = this.name;
12119 input.cls += ' input-' + this.size;
12123 ['xs','sm','md','lg'].map(function(size){
12124 if (settings[size]) {
12125 cfg.cls += ' col-' + size + '-' + settings[size];
12129 var inputblock = input;
12133 cls: 'glyphicon form-control-feedback'
12136 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12139 cls : 'has-feedback',
12147 if (this.before || this.after) {
12150 cls : 'input-group',
12154 if (this.before && typeof(this.before) == 'string') {
12156 inputblock.cn.push({
12158 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12162 if (this.before && typeof(this.before) == 'object') {
12163 this.before = Roo.factory(this.before);
12165 inputblock.cn.push({
12167 cls : 'roo-input-before input-group-prepend input-group-' +
12168 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12172 inputblock.cn.push(input);
12174 if (this.after && typeof(this.after) == 'string') {
12175 inputblock.cn.push({
12177 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12181 if (this.after && typeof(this.after) == 'object') {
12182 this.after = Roo.factory(this.after);
12184 inputblock.cn.push({
12186 cls : 'roo-input-after input-group-append input-group-' +
12187 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12191 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12192 inputblock.cls += ' has-feedback';
12193 inputblock.cn.push(feedback);
12198 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12199 tooltip : 'This field is required'
12201 if (this.allowBlank ) {
12202 indicator.style = this.allowBlank ? ' display:none' : '';
12204 if (align ==='left' && this.fieldLabel.length) {
12206 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12213 cls : 'control-label col-form-label',
12214 html : this.fieldLabel
12225 var labelCfg = cfg.cn[1];
12226 var contentCfg = cfg.cn[2];
12228 if(this.indicatorpos == 'right'){
12233 cls : 'control-label col-form-label',
12237 html : this.fieldLabel
12251 labelCfg = cfg.cn[0];
12252 contentCfg = cfg.cn[1];
12256 if(this.labelWidth > 12){
12257 labelCfg.style = "width: " + this.labelWidth + 'px';
12260 if(this.labelWidth < 13 && this.labelmd == 0){
12261 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12264 if(this.labellg > 0){
12265 labelCfg.cls += ' col-lg-' + this.labellg;
12266 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12269 if(this.labelmd > 0){
12270 labelCfg.cls += ' col-md-' + this.labelmd;
12271 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12274 if(this.labelsm > 0){
12275 labelCfg.cls += ' col-sm-' + this.labelsm;
12276 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12279 if(this.labelxs > 0){
12280 labelCfg.cls += ' col-xs-' + this.labelxs;
12281 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12285 } else if ( this.fieldLabel.length) {
12292 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12293 tooltip : 'This field is required',
12294 style : this.allowBlank ? ' display:none' : ''
12298 //cls : 'input-group-addon',
12299 html : this.fieldLabel
12307 if(this.indicatorpos == 'right'){
12312 //cls : 'input-group-addon',
12313 html : this.fieldLabel
12318 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12319 tooltip : 'This field is required',
12320 style : this.allowBlank ? ' display:none' : ''
12340 if (this.parentType === 'Navbar' && this.parent().bar) {
12341 cfg.cls += ' navbar-form';
12344 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12345 // on BS4 we do this only if not form
12346 cfg.cls += ' navbar-form';
12354 * return the real input element.
12356 inputEl: function ()
12358 return this.el.select('input.form-control',true).first();
12361 tooltipEl : function()
12363 return this.inputEl();
12366 indicatorEl : function()
12368 if (Roo.bootstrap.version == 4) {
12369 return false; // not enabled in v4 yet.
12372 var indicator = this.el.select('i.roo-required-indicator',true).first();
12382 setDisabled : function(v)
12384 var i = this.inputEl().dom;
12386 i.removeAttribute('disabled');
12390 i.setAttribute('disabled','true');
12392 initEvents : function()
12395 this.inputEl().on("keydown" , this.fireKey, this);
12396 this.inputEl().on("focus", this.onFocus, this);
12397 this.inputEl().on("blur", this.onBlur, this);
12399 this.inputEl().relayEvent('keyup', this);
12400 this.inputEl().relayEvent('paste', this);
12402 this.indicator = this.indicatorEl();
12404 if(this.indicator){
12405 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12408 // reference to original value for reset
12409 this.originalValue = this.getValue();
12410 //Roo.form.TextField.superclass.initEvents.call(this);
12411 if(this.validationEvent == 'keyup'){
12412 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12413 this.inputEl().on('keyup', this.filterValidation, this);
12415 else if(this.validationEvent !== false){
12416 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12419 if(this.selectOnFocus){
12420 this.on("focus", this.preFocus, this);
12423 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12424 this.inputEl().on("keypress", this.filterKeys, this);
12426 this.inputEl().relayEvent('keypress', this);
12429 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12430 this.el.on("click", this.autoSize, this);
12433 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12434 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12437 if (typeof(this.before) == 'object') {
12438 this.before.render(this.el.select('.roo-input-before',true).first());
12440 if (typeof(this.after) == 'object') {
12441 this.after.render(this.el.select('.roo-input-after',true).first());
12444 this.inputEl().on('change', this.onChange, this);
12447 filterValidation : function(e){
12448 if(!e.isNavKeyPress()){
12449 this.validationTask.delay(this.validationDelay);
12453 * Validates the field value
12454 * @return {Boolean} True if the value is valid, else false
12456 validate : function(){
12457 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12458 if(this.disabled || this.validateValue(this.getRawValue())){
12463 this.markInvalid();
12469 * Validates a value according to the field's validation rules and marks the field as invalid
12470 * if the validation fails
12471 * @param {Mixed} value The value to validate
12472 * @return {Boolean} True if the value is valid, else false
12474 validateValue : function(value)
12476 if(this.getVisibilityEl().hasClass('hidden')){
12480 if(value.length < 1) { // if it's blank
12481 if(this.allowBlank){
12487 if(value.length < this.minLength){
12490 if(value.length > this.maxLength){
12494 var vt = Roo.form.VTypes;
12495 if(!vt[this.vtype](value, this)){
12499 if(typeof this.validator == "function"){
12500 var msg = this.validator(value);
12504 if (typeof(msg) == 'string') {
12505 this.invalidText = msg;
12509 if(this.regex && !this.regex.test(value)){
12517 fireKey : function(e){
12518 //Roo.log('field ' + e.getKey());
12519 if(e.isNavKeyPress()){
12520 this.fireEvent("specialkey", this, e);
12523 focus : function (selectText){
12525 this.inputEl().focus();
12526 if(selectText === true){
12527 this.inputEl().dom.select();
12533 onFocus : function(){
12534 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12535 // this.el.addClass(this.focusClass);
12537 if(!this.hasFocus){
12538 this.hasFocus = true;
12539 this.startValue = this.getValue();
12540 this.fireEvent("focus", this);
12544 beforeBlur : Roo.emptyFn,
12548 onBlur : function(){
12550 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12551 //this.el.removeClass(this.focusClass);
12553 this.hasFocus = false;
12554 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12557 var v = this.getValue();
12558 if(String(v) !== String(this.startValue)){
12559 this.fireEvent('change', this, v, this.startValue);
12561 this.fireEvent("blur", this);
12564 onChange : function(e)
12566 var v = this.getValue();
12567 if(String(v) !== String(this.startValue)){
12568 this.fireEvent('change', this, v, this.startValue);
12574 * Resets the current field value to the originally loaded value and clears any validation messages
12576 reset : function(){
12577 this.setValue(this.originalValue);
12581 * Returns the name of the field
12582 * @return {Mixed} name The name field
12584 getName: function(){
12588 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12589 * @return {Mixed} value The field value
12591 getValue : function(){
12593 var v = this.inputEl().getValue();
12598 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
12599 * @return {Mixed} value The field value
12601 getRawValue : function(){
12602 var v = this.inputEl().getValue();
12608 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
12609 * @param {Mixed} value The value to set
12611 setRawValue : function(v){
12612 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12615 selectText : function(start, end){
12616 var v = this.getRawValue();
12618 start = start === undefined ? 0 : start;
12619 end = end === undefined ? v.length : end;
12620 var d = this.inputEl().dom;
12621 if(d.setSelectionRange){
12622 d.setSelectionRange(start, end);
12623 }else if(d.createTextRange){
12624 var range = d.createTextRange();
12625 range.moveStart("character", start);
12626 range.moveEnd("character", v.length-end);
12633 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
12634 * @param {Mixed} value The value to set
12636 setValue : function(v){
12639 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12645 processValue : function(value){
12646 if(this.stripCharsRe){
12647 var newValue = value.replace(this.stripCharsRe, '');
12648 if(newValue !== value){
12649 this.setRawValue(newValue);
12656 preFocus : function(){
12658 if(this.selectOnFocus){
12659 this.inputEl().dom.select();
12662 filterKeys : function(e){
12663 var k = e.getKey();
12664 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12667 var c = e.getCharCode(), cc = String.fromCharCode(c);
12668 if(Roo.isIE && (e.isSpecialKey() || !cc)){
12671 if(!this.maskRe.test(cc)){
12676 * Clear any invalid styles/messages for this field
12678 clearInvalid : function(){
12680 if(!this.el || this.preventMark){ // not rendered
12685 this.el.removeClass([this.invalidClass, 'is-invalid']);
12687 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12689 var feedback = this.el.select('.form-control-feedback', true).first();
12692 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12697 if(this.indicator){
12698 this.indicator.removeClass('visible');
12699 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12702 this.fireEvent('valid', this);
12706 * Mark this field as valid
12708 markValid : function()
12710 if(!this.el || this.preventMark){ // not rendered...
12714 this.el.removeClass([this.invalidClass, this.validClass]);
12715 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12717 var feedback = this.el.select('.form-control-feedback', true).first();
12720 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12723 if(this.indicator){
12724 this.indicator.removeClass('visible');
12725 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12733 if(this.allowBlank && !this.getRawValue().length){
12736 if (Roo.bootstrap.version == 3) {
12737 this.el.addClass(this.validClass);
12739 this.inputEl().addClass('is-valid');
12742 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12744 var feedback = this.el.select('.form-control-feedback', true).first();
12747 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12748 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12753 this.fireEvent('valid', this);
12757 * Mark this field as invalid
12758 * @param {String} msg The validation message
12760 markInvalid : function(msg)
12762 if(!this.el || this.preventMark){ // not rendered
12766 this.el.removeClass([this.invalidClass, this.validClass]);
12767 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12769 var feedback = this.el.select('.form-control-feedback', true).first();
12772 this.el.select('.form-control-feedback', true).first().removeClass(
12773 [this.invalidFeedbackClass, this.validFeedbackClass]);
12780 if(this.allowBlank && !this.getRawValue().length){
12784 if(this.indicator){
12785 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12786 this.indicator.addClass('visible');
12788 if (Roo.bootstrap.version == 3) {
12789 this.el.addClass(this.invalidClass);
12791 this.inputEl().addClass('is-invalid');
12796 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12798 var feedback = this.el.select('.form-control-feedback', true).first();
12801 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12803 if(this.getValue().length || this.forceFeedback){
12804 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12811 this.fireEvent('invalid', this, msg);
12814 SafariOnKeyDown : function(event)
12816 // this is a workaround for a password hang bug on chrome/ webkit.
12817 if (this.inputEl().dom.type != 'password') {
12821 var isSelectAll = false;
12823 if(this.inputEl().dom.selectionEnd > 0){
12824 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12826 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12827 event.preventDefault();
12832 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12834 event.preventDefault();
12835 // this is very hacky as keydown always get's upper case.
12837 var cc = String.fromCharCode(event.getCharCode());
12838 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
12842 adjustWidth : function(tag, w){
12843 tag = tag.toLowerCase();
12844 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12845 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12846 if(tag == 'input'){
12849 if(tag == 'textarea'){
12852 }else if(Roo.isOpera){
12853 if(tag == 'input'){
12856 if(tag == 'textarea'){
12864 setFieldLabel : function(v)
12866 if(!this.rendered){
12870 if(this.indicatorEl()){
12871 var ar = this.el.select('label > span',true);
12873 if (ar.elements.length) {
12874 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12875 this.fieldLabel = v;
12879 var br = this.el.select('label',true);
12881 if(br.elements.length) {
12882 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12883 this.fieldLabel = v;
12887 Roo.log('Cannot Found any of label > span || label in input');
12891 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12892 this.fieldLabel = v;
12907 * @class Roo.bootstrap.TextArea
12908 * @extends Roo.bootstrap.Input
12909 * Bootstrap TextArea class
12910 * @cfg {Number} cols Specifies the visible width of a text area
12911 * @cfg {Number} rows Specifies the visible number of lines in a text area
12912 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12913 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12914 * @cfg {string} html text
12917 * Create a new TextArea
12918 * @param {Object} config The config object
12921 Roo.bootstrap.TextArea = function(config){
12922 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12926 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
12936 getAutoCreate : function(){
12938 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12944 if(this.inputType != 'hidden'){
12945 cfg.cls = 'form-group' //input-group
12953 value : this.value || '',
12954 html: this.html || '',
12955 cls : 'form-control',
12956 placeholder : this.placeholder || ''
12960 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12961 input.maxLength = this.maxLength;
12965 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12969 input.cols = this.cols;
12972 if (this.readOnly) {
12973 input.readonly = true;
12977 input.name = this.name;
12981 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12985 ['xs','sm','md','lg'].map(function(size){
12986 if (settings[size]) {
12987 cfg.cls += ' col-' + size + '-' + settings[size];
12991 var inputblock = input;
12993 if(this.hasFeedback && !this.allowBlank){
12997 cls: 'glyphicon form-control-feedback'
13001 cls : 'has-feedback',
13010 if (this.before || this.after) {
13013 cls : 'input-group',
13017 inputblock.cn.push({
13019 cls : 'input-group-addon',
13024 inputblock.cn.push(input);
13026 if(this.hasFeedback && !this.allowBlank){
13027 inputblock.cls += ' has-feedback';
13028 inputblock.cn.push(feedback);
13032 inputblock.cn.push({
13034 cls : 'input-group-addon',
13041 if (align ==='left' && this.fieldLabel.length) {
13046 cls : 'control-label',
13047 html : this.fieldLabel
13058 if(this.labelWidth > 12){
13059 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13062 if(this.labelWidth < 13 && this.labelmd == 0){
13063 this.labelmd = this.labelWidth;
13066 if(this.labellg > 0){
13067 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13068 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13071 if(this.labelmd > 0){
13072 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13073 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13076 if(this.labelsm > 0){
13077 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13078 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13081 if(this.labelxs > 0){
13082 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13083 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13086 } else if ( this.fieldLabel.length) {
13091 //cls : 'input-group-addon',
13092 html : this.fieldLabel
13110 if (this.disabled) {
13111 input.disabled=true;
13118 * return the real textarea element.
13120 inputEl: function ()
13122 return this.el.select('textarea.form-control',true).first();
13126 * Clear any invalid styles/messages for this field
13128 clearInvalid : function()
13131 if(!this.el || this.preventMark){ // not rendered
13135 var label = this.el.select('label', true).first();
13136 var icon = this.el.select('i.fa-star', true).first();
13141 this.el.removeClass( this.validClass);
13142 this.inputEl().removeClass('is-invalid');
13144 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13146 var feedback = this.el.select('.form-control-feedback', true).first();
13149 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13154 this.fireEvent('valid', this);
13158 * Mark this field as valid
13160 markValid : function()
13162 if(!this.el || this.preventMark){ // not rendered
13166 this.el.removeClass([this.invalidClass, this.validClass]);
13167 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13169 var feedback = this.el.select('.form-control-feedback', true).first();
13172 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13175 if(this.disabled || this.allowBlank){
13179 var label = this.el.select('label', true).first();
13180 var icon = this.el.select('i.fa-star', true).first();
13185 if (Roo.bootstrap.version == 3) {
13186 this.el.addClass(this.validClass);
13188 this.inputEl().addClass('is-valid');
13192 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13194 var feedback = this.el.select('.form-control-feedback', true).first();
13197 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13198 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13203 this.fireEvent('valid', this);
13207 * Mark this field as invalid
13208 * @param {String} msg The validation message
13210 markInvalid : function(msg)
13212 if(!this.el || this.preventMark){ // not rendered
13216 this.el.removeClass([this.invalidClass, this.validClass]);
13217 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13219 var feedback = this.el.select('.form-control-feedback', true).first();
13222 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13225 if(this.disabled || this.allowBlank){
13229 var label = this.el.select('label', true).first();
13230 var icon = this.el.select('i.fa-star', true).first();
13232 if(!this.getValue().length && label && !icon){
13233 this.el.createChild({
13235 cls : 'text-danger fa fa-lg fa-star',
13236 tooltip : 'This field is required',
13237 style : 'margin-right:5px;'
13241 if (Roo.bootstrap.version == 3) {
13242 this.el.addClass(this.invalidClass);
13244 this.inputEl().addClass('is-invalid');
13247 // fixme ... this may be depricated need to test..
13248 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13250 var feedback = this.el.select('.form-control-feedback', true).first();
13253 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13255 if(this.getValue().length || this.forceFeedback){
13256 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13263 this.fireEvent('invalid', this, msg);
13271 * trigger field - base class for combo..
13276 * @class Roo.bootstrap.TriggerField
13277 * @extends Roo.bootstrap.Input
13278 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13279 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13280 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13281 * for which you can provide a custom implementation. For example:
13283 var trigger = new Roo.bootstrap.TriggerField();
13284 trigger.onTriggerClick = myTriggerFn;
13285 trigger.applyTo('my-field');
13288 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13289 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13290 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13291 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13292 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13295 * Create a new TriggerField.
13296 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13297 * to the base TextField)
13299 Roo.bootstrap.TriggerField = function(config){
13300 this.mimicing = false;
13301 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13304 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
13306 * @cfg {String} triggerClass A CSS class to apply to the trigger
13309 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13314 * @cfg {Boolean} removable (true|false) special filter default false
13318 /** @cfg {Boolean} grow @hide */
13319 /** @cfg {Number} growMin @hide */
13320 /** @cfg {Number} growMax @hide */
13326 autoSize: Roo.emptyFn,
13330 deferHeight : true,
13333 actionMode : 'wrap',
13338 getAutoCreate : function(){
13340 var align = this.labelAlign || this.parentLabelAlign();
13345 cls: 'form-group' //input-group
13352 type : this.inputType,
13353 cls : 'form-control',
13354 autocomplete: 'new-password',
13355 placeholder : this.placeholder || ''
13359 input.name = this.name;
13362 input.cls += ' input-' + this.size;
13365 if (this.disabled) {
13366 input.disabled=true;
13369 var inputblock = input;
13371 if(this.hasFeedback && !this.allowBlank){
13375 cls: 'glyphicon form-control-feedback'
13378 if(this.removable && !this.editable ){
13380 cls : 'has-feedback',
13386 cls : 'roo-combo-removable-btn close'
13393 cls : 'has-feedback',
13402 if(this.removable && !this.editable ){
13404 cls : 'roo-removable',
13410 cls : 'roo-combo-removable-btn close'
13417 if (this.before || this.after) {
13420 cls : 'input-group',
13424 inputblock.cn.push({
13426 cls : 'input-group-addon input-group-prepend input-group-text',
13431 inputblock.cn.push(input);
13433 if(this.hasFeedback && !this.allowBlank){
13434 inputblock.cls += ' has-feedback';
13435 inputblock.cn.push(feedback);
13439 inputblock.cn.push({
13441 cls : 'input-group-addon input-group-append input-group-text',
13450 var ibwrap = inputblock;
13455 cls: 'roo-select2-choices',
13459 cls: 'roo-select2-search-field',
13471 cls: 'roo-select2-container input-group',
13476 cls: 'form-hidden-field'
13482 if(!this.multiple && this.showToggleBtn){
13488 if (this.caret != false) {
13491 cls: 'fa fa-' + this.caret
13498 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13500 Roo.bootstrap.version == 3 ? caret : '',
13503 cls: 'combobox-clear',
13517 combobox.cls += ' roo-select2-container-multi';
13521 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13522 tooltip : 'This field is required'
13524 if (Roo.bootstrap.version == 4) {
13527 style : 'display:none'
13532 if (align ==='left' && this.fieldLabel.length) {
13534 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13541 cls : 'control-label',
13542 html : this.fieldLabel
13554 var labelCfg = cfg.cn[1];
13555 var contentCfg = cfg.cn[2];
13557 if(this.indicatorpos == 'right'){
13562 cls : 'control-label',
13566 html : this.fieldLabel
13580 labelCfg = cfg.cn[0];
13581 contentCfg = cfg.cn[1];
13584 if(this.labelWidth > 12){
13585 labelCfg.style = "width: " + this.labelWidth + 'px';
13588 if(this.labelWidth < 13 && this.labelmd == 0){
13589 this.labelmd = this.labelWidth;
13592 if(this.labellg > 0){
13593 labelCfg.cls += ' col-lg-' + this.labellg;
13594 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13597 if(this.labelmd > 0){
13598 labelCfg.cls += ' col-md-' + this.labelmd;
13599 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13602 if(this.labelsm > 0){
13603 labelCfg.cls += ' col-sm-' + this.labelsm;
13604 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13607 if(this.labelxs > 0){
13608 labelCfg.cls += ' col-xs-' + this.labelxs;
13609 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13612 } else if ( this.fieldLabel.length) {
13613 // Roo.log(" label");
13618 //cls : 'input-group-addon',
13619 html : this.fieldLabel
13627 if(this.indicatorpos == 'right'){
13635 html : this.fieldLabel
13649 // Roo.log(" no label && no align");
13656 ['xs','sm','md','lg'].map(function(size){
13657 if (settings[size]) {
13658 cfg.cls += ' col-' + size + '-' + settings[size];
13669 onResize : function(w, h){
13670 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13671 // if(typeof w == 'number'){
13672 // var x = w - this.trigger.getWidth();
13673 // this.inputEl().setWidth(this.adjustWidth('input', x));
13674 // this.trigger.setStyle('left', x+'px');
13679 adjustSize : Roo.BoxComponent.prototype.adjustSize,
13682 getResizeEl : function(){
13683 return this.inputEl();
13687 getPositionEl : function(){
13688 return this.inputEl();
13692 alignErrorIcon : function(){
13693 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13697 initEvents : function(){
13701 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13702 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13703 if(!this.multiple && this.showToggleBtn){
13704 this.trigger = this.el.select('span.dropdown-toggle',true).first();
13705 if(this.hideTrigger){
13706 this.trigger.setDisplayed(false);
13708 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13712 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13715 if(this.removable && !this.editable && !this.tickable){
13716 var close = this.closeTriggerEl();
13719 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13720 close.on('click', this.removeBtnClick, this, close);
13724 //this.trigger.addClassOnOver('x-form-trigger-over');
13725 //this.trigger.addClassOnClick('x-form-trigger-click');
13728 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13732 closeTriggerEl : function()
13734 var close = this.el.select('.roo-combo-removable-btn', true).first();
13735 return close ? close : false;
13738 removeBtnClick : function(e, h, el)
13740 e.preventDefault();
13742 if(this.fireEvent("remove", this) !== false){
13744 this.fireEvent("afterremove", this)
13748 createList : function()
13750 this.list = Roo.get(document.body).createChild({
13751 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13752 cls: 'typeahead typeahead-long dropdown-menu shadow',
13753 style: 'display:none'
13756 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13761 initTrigger : function(){
13766 onDestroy : function(){
13768 this.trigger.removeAllListeners();
13769 // this.trigger.remove();
13772 // this.wrap.remove();
13774 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13778 onFocus : function(){
13779 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13781 if(!this.mimicing){
13782 this.wrap.addClass('x-trigger-wrap-focus');
13783 this.mimicing = true;
13784 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13785 if(this.monitorTab){
13786 this.el.on("keydown", this.checkTab, this);
13793 checkTab : function(e){
13794 if(e.getKey() == e.TAB){
13795 this.triggerBlur();
13800 onBlur : function(){
13805 mimicBlur : function(e, t){
13807 if(!this.wrap.contains(t) && this.validateBlur()){
13808 this.triggerBlur();
13814 triggerBlur : function(){
13815 this.mimicing = false;
13816 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13817 if(this.monitorTab){
13818 this.el.un("keydown", this.checkTab, this);
13820 //this.wrap.removeClass('x-trigger-wrap-focus');
13821 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13825 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13826 validateBlur : function(e, t){
13831 onDisable : function(){
13832 this.inputEl().dom.disabled = true;
13833 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13835 // this.wrap.addClass('x-item-disabled');
13840 onEnable : function(){
13841 this.inputEl().dom.disabled = false;
13842 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13844 // this.el.removeClass('x-item-disabled');
13849 onShow : function(){
13850 var ae = this.getActionEl();
13853 ae.dom.style.display = '';
13854 ae.dom.style.visibility = 'visible';
13860 onHide : function(){
13861 var ae = this.getActionEl();
13862 ae.dom.style.display = 'none';
13866 * The function that should handle the trigger's click event. This method does nothing by default until overridden
13867 * by an implementing function.
13869 * @param {EventObject} e
13871 onTriggerClick : Roo.emptyFn
13879 * @class Roo.bootstrap.CardUploader
13880 * @extends Roo.bootstrap.Button
13881 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13882 * @cfg {Number} errorTimeout default 3000
13883 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
13884 * @cfg {Array} html The button text.
13888 * Create a new CardUploader
13889 * @param {Object} config The config object
13892 Roo.bootstrap.CardUploader = function(config){
13896 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13899 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
13907 * When a image is clicked on - and needs to display a slideshow or similar..
13908 * @param {Roo.bootstrap.Card} this
13909 * @param {Object} The image information data
13915 * When a the download link is clicked
13916 * @param {Roo.bootstrap.Card} this
13917 * @param {Object} The image information data contains
13924 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
13927 errorTimeout : 3000,
13931 fileCollection : false,
13934 getAutoCreate : function()
13938 cls :'form-group' ,
13943 //cls : 'input-group-addon',
13944 html : this.fieldLabel
13952 value : this.value,
13953 cls : 'd-none form-control'
13958 multiple : 'multiple',
13960 cls : 'd-none roo-card-upload-selector'
13964 cls : 'roo-card-uploader-button-container w-100 mb-2'
13967 cls : 'card-columns roo-card-uploader-container'
13977 getChildContainer : function() /// what children are added to.
13979 return this.containerEl;
13982 getButtonContainer : function() /// what children are added to.
13984 return this.el.select(".roo-card-uploader-button-container").first();
13987 initEvents : function()
13990 Roo.bootstrap.Input.prototype.initEvents.call(this);
13994 xns: Roo.bootstrap,
13997 container_method : 'getButtonContainer' ,
13998 html : this.html, // fix changable?
14001 'click' : function(btn, e) {
14010 this.urlAPI = (window.createObjectURL && window) ||
14011 (window.URL && URL.revokeObjectURL && URL) ||
14012 (window.webkitURL && webkitURL);
14017 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14019 this.selectorEl.on('change', this.onFileSelected, this);
14022 this.images.forEach(function(img) {
14025 this.images = false;
14027 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14033 onClick : function(e)
14035 e.preventDefault();
14037 this.selectorEl.dom.click();
14041 onFileSelected : function(e)
14043 e.preventDefault();
14045 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14049 Roo.each(this.selectorEl.dom.files, function(file){
14050 this.addFile(file);
14059 addFile : function(file)
14062 if(typeof(file) === 'string'){
14063 throw "Add file by name?"; // should not happen
14067 if(!file || !this.urlAPI){
14077 var url = _this.urlAPI.createObjectURL( file);
14080 id : Roo.bootstrap.CardUploader.ID--,
14081 is_uploaded : false,
14085 mimetype : file.type,
14093 * addCard - add an Attachment to the uploader
14094 * @param data - the data about the image to upload
14098 title : "Title of file",
14099 is_uploaded : false,
14100 src : "http://.....",
14101 srcfile : { the File upload object },
14102 mimetype : file.type,
14105 .. any other data...
14111 addCard : function (data)
14113 // hidden input element?
14114 // if the file is not an image...
14115 //then we need to use something other that and header_image
14120 xns : Roo.bootstrap,
14121 xtype : 'CardFooter',
14124 xns : Roo.bootstrap,
14130 xns : Roo.bootstrap,
14132 html : String.format("<small>{0}</small>", data.title),
14133 cls : 'col-10 text-left',
14138 click : function() {
14140 t.fireEvent( "download", t, data );
14146 xns : Roo.bootstrap,
14148 style: 'max-height: 28px; ',
14154 click : function() {
14155 t.removeCard(data.id)
14167 var cn = this.addxtype(
14170 xns : Roo.bootstrap,
14173 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14174 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14175 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14180 initEvents : function() {
14181 Roo.bootstrap.Card.prototype.initEvents.call(this);
14183 this.imgEl = this.el.select('.card-img-top').first();
14185 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14186 this.imgEl.set({ 'pointer' : 'cursor' });
14189 this.getCardFooter().addClass('p-1');
14196 // dont' really need ot update items.
14197 // this.items.push(cn);
14198 this.fileCollection.add(cn);
14200 if (!data.srcfile) {
14201 this.updateInput();
14206 var reader = new FileReader();
14207 reader.addEventListener("load", function() {
14208 data.srcdata = reader.result;
14211 reader.readAsDataURL(data.srcfile);
14216 removeCard : function(id)
14219 var card = this.fileCollection.get(id);
14220 card.data.is_deleted = 1;
14221 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14222 //this.fileCollection.remove(card);
14223 //this.items = this.items.filter(function(e) { return e != card });
14224 // dont' really need ot update items.
14225 card.el.dom.parentNode.removeChild(card.el.dom);
14226 this.updateInput();
14232 this.fileCollection.each(function(card) {
14233 if (card.el.dom && card.el.dom.parentNode) {
14234 card.el.dom.parentNode.removeChild(card.el.dom);
14237 this.fileCollection.clear();
14238 this.updateInput();
14241 updateInput : function()
14244 this.fileCollection.each(function(e) {
14248 this.inputEl().dom.value = JSON.stringify(data);
14258 Roo.bootstrap.CardUploader.ID = -1;/*
14260 * Ext JS Library 1.1.1
14261 * Copyright(c) 2006-2007, Ext JS, LLC.
14263 * Originally Released Under LGPL - original licence link has changed is not relivant.
14266 * <script type="text/javascript">
14271 * @class Roo.data.SortTypes
14273 * Defines the default sorting (casting?) comparison functions used when sorting data.
14275 Roo.data.SortTypes = {
14277 * Default sort that does nothing
14278 * @param {Mixed} s The value being converted
14279 * @return {Mixed} The comparison value
14281 none : function(s){
14286 * The regular expression used to strip tags
14290 stripTagsRE : /<\/?[^>]+>/gi,
14293 * Strips all HTML tags to sort on text only
14294 * @param {Mixed} s The value being converted
14295 * @return {String} The comparison value
14297 asText : function(s){
14298 return String(s).replace(this.stripTagsRE, "");
14302 * Strips all HTML tags to sort on text only - Case insensitive
14303 * @param {Mixed} s The value being converted
14304 * @return {String} The comparison value
14306 asUCText : function(s){
14307 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14311 * Case insensitive string
14312 * @param {Mixed} s The value being converted
14313 * @return {String} The comparison value
14315 asUCString : function(s) {
14316 return String(s).toUpperCase();
14321 * @param {Mixed} s The value being converted
14322 * @return {Number} The comparison value
14324 asDate : function(s) {
14328 if(s instanceof Date){
14329 return s.getTime();
14331 return Date.parse(String(s));
14336 * @param {Mixed} s The value being converted
14337 * @return {Float} The comparison value
14339 asFloat : function(s) {
14340 var val = parseFloat(String(s).replace(/,/g, ""));
14349 * @param {Mixed} s The value being converted
14350 * @return {Number} The comparison value
14352 asInt : function(s) {
14353 var val = parseInt(String(s).replace(/,/g, ""));
14361 * Ext JS Library 1.1.1
14362 * Copyright(c) 2006-2007, Ext JS, LLC.
14364 * Originally Released Under LGPL - original licence link has changed is not relivant.
14367 * <script type="text/javascript">
14371 * @class Roo.data.Record
14372 * Instances of this class encapsulate both record <em>definition</em> information, and record
14373 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14374 * to access Records cached in an {@link Roo.data.Store} object.<br>
14376 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14377 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14380 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14382 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14383 * {@link #create}. The parameters are the same.
14384 * @param {Array} data An associative Array of data values keyed by the field name.
14385 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14386 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14387 * not specified an integer id is generated.
14389 Roo.data.Record = function(data, id){
14390 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14395 * Generate a constructor for a specific record layout.
14396 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14397 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14398 * Each field definition object may contain the following properties: <ul>
14399 * <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,
14400 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14401 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14402 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14403 * is being used, then this is a string containing the javascript expression to reference the data relative to
14404 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14405 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14406 * this may be omitted.</p></li>
14407 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14408 * <ul><li>auto (Default, implies no conversion)</li>
14413 * <li>date</li></ul></p></li>
14414 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14415 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14416 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14417 * by the Reader into an object that will be stored in the Record. It is passed the
14418 * following parameters:<ul>
14419 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14421 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14423 * <br>usage:<br><pre><code>
14424 var TopicRecord = Roo.data.Record.create(
14425 {name: 'title', mapping: 'topic_title'},
14426 {name: 'author', mapping: 'username'},
14427 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14428 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14429 {name: 'lastPoster', mapping: 'user2'},
14430 {name: 'excerpt', mapping: 'post_text'}
14433 var myNewRecord = new TopicRecord({
14434 title: 'Do my job please',
14437 lastPost: new Date(),
14438 lastPoster: 'Animal',
14439 excerpt: 'No way dude!'
14441 myStore.add(myNewRecord);
14446 Roo.data.Record.create = function(o){
14447 var f = function(){
14448 f.superclass.constructor.apply(this, arguments);
14450 Roo.extend(f, Roo.data.Record);
14451 var p = f.prototype;
14452 p.fields = new Roo.util.MixedCollection(false, function(field){
14455 for(var i = 0, len = o.length; i < len; i++){
14456 p.fields.add(new Roo.data.Field(o[i]));
14458 f.getField = function(name){
14459 return p.fields.get(name);
14464 Roo.data.Record.AUTO_ID = 1000;
14465 Roo.data.Record.EDIT = 'edit';
14466 Roo.data.Record.REJECT = 'reject';
14467 Roo.data.Record.COMMIT = 'commit';
14469 Roo.data.Record.prototype = {
14471 * Readonly flag - true if this record has been modified.
14480 join : function(store){
14481 this.store = store;
14485 * Set the named field to the specified value.
14486 * @param {String} name The name of the field to set.
14487 * @param {Object} value The value to set the field to.
14489 set : function(name, value){
14490 if(this.data[name] == value){
14494 if(!this.modified){
14495 this.modified = {};
14497 if(typeof this.modified[name] == 'undefined'){
14498 this.modified[name] = this.data[name];
14500 this.data[name] = value;
14501 if(!this.editing && this.store){
14502 this.store.afterEdit(this);
14507 * Get the value of the named field.
14508 * @param {String} name The name of the field to get the value of.
14509 * @return {Object} The value of the field.
14511 get : function(name){
14512 return this.data[name];
14516 beginEdit : function(){
14517 this.editing = true;
14518 this.modified = {};
14522 cancelEdit : function(){
14523 this.editing = false;
14524 delete this.modified;
14528 endEdit : function(){
14529 this.editing = false;
14530 if(this.dirty && this.store){
14531 this.store.afterEdit(this);
14536 * Usually called by the {@link Roo.data.Store} which owns the Record.
14537 * Rejects all changes made to the Record since either creation, or the last commit operation.
14538 * Modified fields are reverted to their original values.
14540 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14541 * of reject operations.
14543 reject : function(){
14544 var m = this.modified;
14546 if(typeof m[n] != "function"){
14547 this.data[n] = m[n];
14550 this.dirty = false;
14551 delete this.modified;
14552 this.editing = false;
14554 this.store.afterReject(this);
14559 * Usually called by the {@link Roo.data.Store} which owns the Record.
14560 * Commits all changes made to the Record since either creation, or the last commit operation.
14562 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14563 * of commit operations.
14565 commit : function(){
14566 this.dirty = false;
14567 delete this.modified;
14568 this.editing = false;
14570 this.store.afterCommit(this);
14575 hasError : function(){
14576 return this.error != null;
14580 clearError : function(){
14585 * Creates a copy of this record.
14586 * @param {String} id (optional) A new record id if you don't want to use this record's id
14589 copy : function(newId) {
14590 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14594 * Ext JS Library 1.1.1
14595 * Copyright(c) 2006-2007, Ext JS, LLC.
14597 * Originally Released Under LGPL - original licence link has changed is not relivant.
14600 * <script type="text/javascript">
14606 * @class Roo.data.Store
14607 * @extends Roo.util.Observable
14608 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14609 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14611 * 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
14612 * has no knowledge of the format of the data returned by the Proxy.<br>
14614 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14615 * instances from the data object. These records are cached and made available through accessor functions.
14617 * Creates a new Store.
14618 * @param {Object} config A config object containing the objects needed for the Store to access data,
14619 * and read the data into Records.
14621 Roo.data.Store = function(config){
14622 this.data = new Roo.util.MixedCollection(false);
14623 this.data.getKey = function(o){
14626 this.baseParams = {};
14628 this.paramNames = {
14633 "multisort" : "_multisort"
14636 if(config && config.data){
14637 this.inlineData = config.data;
14638 delete config.data;
14641 Roo.apply(this, config);
14643 if(this.reader){ // reader passed
14644 this.reader = Roo.factory(this.reader, Roo.data);
14645 this.reader.xmodule = this.xmodule || false;
14646 if(!this.recordType){
14647 this.recordType = this.reader.recordType;
14649 if(this.reader.onMetaChange){
14650 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14654 if(this.recordType){
14655 this.fields = this.recordType.prototype.fields;
14657 this.modified = [];
14661 * @event datachanged
14662 * Fires when the data cache has changed, and a widget which is using this Store
14663 * as a Record cache should refresh its view.
14664 * @param {Store} this
14666 datachanged : true,
14668 * @event metachange
14669 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14670 * @param {Store} this
14671 * @param {Object} meta The JSON metadata
14676 * Fires when Records have been added to the Store
14677 * @param {Store} this
14678 * @param {Roo.data.Record[]} records The array of Records added
14679 * @param {Number} index The index at which the record(s) were added
14684 * Fires when a Record has been removed from the Store
14685 * @param {Store} this
14686 * @param {Roo.data.Record} record The Record that was removed
14687 * @param {Number} index The index at which the record was removed
14692 * Fires when a Record has been updated
14693 * @param {Store} this
14694 * @param {Roo.data.Record} record The Record that was updated
14695 * @param {String} operation The update operation being performed. Value may be one of:
14697 Roo.data.Record.EDIT
14698 Roo.data.Record.REJECT
14699 Roo.data.Record.COMMIT
14705 * Fires when the data cache has been cleared.
14706 * @param {Store} this
14710 * @event beforeload
14711 * Fires before a request is made for a new data object. If the beforeload handler returns false
14712 * the load action will be canceled.
14713 * @param {Store} this
14714 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14718 * @event beforeloadadd
14719 * Fires after a new set of Records has been loaded.
14720 * @param {Store} this
14721 * @param {Roo.data.Record[]} records The Records that were loaded
14722 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14724 beforeloadadd : true,
14727 * Fires after a new set of Records has been loaded, before they are added to the store.
14728 * @param {Store} this
14729 * @param {Roo.data.Record[]} records The Records that were loaded
14730 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14731 * @params {Object} return from reader
14735 * @event loadexception
14736 * Fires if an exception occurs in the Proxy during loading.
14737 * Called with the signature of the Proxy's "loadexception" event.
14738 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14741 * @param {Object} return from JsonData.reader() - success, totalRecords, records
14742 * @param {Object} load options
14743 * @param {Object} jsonData from your request (normally this contains the Exception)
14745 loadexception : true
14749 this.proxy = Roo.factory(this.proxy, Roo.data);
14750 this.proxy.xmodule = this.xmodule || false;
14751 this.relayEvents(this.proxy, ["loadexception"]);
14753 this.sortToggle = {};
14754 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14756 Roo.data.Store.superclass.constructor.call(this);
14758 if(this.inlineData){
14759 this.loadData(this.inlineData);
14760 delete this.inlineData;
14764 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14766 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
14767 * without a remote query - used by combo/forms at present.
14771 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
14774 * @cfg {Array} data Inline data to be loaded when the store is initialized.
14777 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
14778 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14781 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14782 * on any HTTP request
14785 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14788 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14792 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14793 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14795 remoteSort : false,
14798 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14799 * loaded or when a record is removed. (defaults to false).
14801 pruneModifiedRecords : false,
14804 lastOptions : null,
14807 * Add Records to the Store and fires the add event.
14808 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14810 add : function(records){
14811 records = [].concat(records);
14812 for(var i = 0, len = records.length; i < len; i++){
14813 records[i].join(this);
14815 var index = this.data.length;
14816 this.data.addAll(records);
14817 this.fireEvent("add", this, records, index);
14821 * Remove a Record from the Store and fires the remove event.
14822 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14824 remove : function(record){
14825 var index = this.data.indexOf(record);
14826 this.data.removeAt(index);
14828 if(this.pruneModifiedRecords){
14829 this.modified.remove(record);
14831 this.fireEvent("remove", this, record, index);
14835 * Remove all Records from the Store and fires the clear event.
14837 removeAll : function(){
14839 if(this.pruneModifiedRecords){
14840 this.modified = [];
14842 this.fireEvent("clear", this);
14846 * Inserts Records to the Store at the given index and fires the add event.
14847 * @param {Number} index The start index at which to insert the passed Records.
14848 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14850 insert : function(index, records){
14851 records = [].concat(records);
14852 for(var i = 0, len = records.length; i < len; i++){
14853 this.data.insert(index, records[i]);
14854 records[i].join(this);
14856 this.fireEvent("add", this, records, index);
14860 * Get the index within the cache of the passed Record.
14861 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14862 * @return {Number} The index of the passed Record. Returns -1 if not found.
14864 indexOf : function(record){
14865 return this.data.indexOf(record);
14869 * Get the index within the cache of the Record with the passed id.
14870 * @param {String} id The id of the Record to find.
14871 * @return {Number} The index of the Record. Returns -1 if not found.
14873 indexOfId : function(id){
14874 return this.data.indexOfKey(id);
14878 * Get the Record with the specified id.
14879 * @param {String} id The id of the Record to find.
14880 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14882 getById : function(id){
14883 return this.data.key(id);
14887 * Get the Record at the specified index.
14888 * @param {Number} index The index of the Record to find.
14889 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14891 getAt : function(index){
14892 return this.data.itemAt(index);
14896 * Returns a range of Records between specified indices.
14897 * @param {Number} startIndex (optional) The starting index (defaults to 0)
14898 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14899 * @return {Roo.data.Record[]} An array of Records
14901 getRange : function(start, end){
14902 return this.data.getRange(start, end);
14906 storeOptions : function(o){
14907 o = Roo.apply({}, o);
14910 this.lastOptions = o;
14914 * Loads the Record cache from the configured Proxy using the configured Reader.
14916 * If using remote paging, then the first load call must specify the <em>start</em>
14917 * and <em>limit</em> properties in the options.params property to establish the initial
14918 * position within the dataset, and the number of Records to cache on each read from the Proxy.
14920 * <strong>It is important to note that for remote data sources, loading is asynchronous,
14921 * and this call will return before the new data has been loaded. Perform any post-processing
14922 * in a callback function, or in a "load" event handler.</strong>
14924 * @param {Object} options An object containing properties which control loading options:<ul>
14925 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14926 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14927 * passed the following arguments:<ul>
14928 * <li>r : Roo.data.Record[]</li>
14929 * <li>options: Options object from the load call</li>
14930 * <li>success: Boolean success indicator</li></ul></li>
14931 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14932 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14935 load : function(options){
14936 options = options || {};
14937 if(this.fireEvent("beforeload", this, options) !== false){
14938 this.storeOptions(options);
14939 var p = Roo.apply(options.params || {}, this.baseParams);
14940 // if meta was not loaded from remote source.. try requesting it.
14941 if (!this.reader.metaFromRemote) {
14942 p._requestMeta = 1;
14944 if(this.sortInfo && this.remoteSort){
14945 var pn = this.paramNames;
14946 p[pn["sort"]] = this.sortInfo.field;
14947 p[pn["dir"]] = this.sortInfo.direction;
14949 if (this.multiSort) {
14950 var pn = this.paramNames;
14951 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14954 this.proxy.load(p, this.reader, this.loadRecords, this, options);
14959 * Reloads the Record cache from the configured Proxy using the configured Reader and
14960 * the options from the last load operation performed.
14961 * @param {Object} options (optional) An object containing properties which may override the options
14962 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14963 * the most recently used options are reused).
14965 reload : function(options){
14966 this.load(Roo.applyIf(options||{}, this.lastOptions));
14970 // Called as a callback by the Reader during a load operation.
14971 loadRecords : function(o, options, success){
14972 if(!o || success === false){
14973 if(success !== false){
14974 this.fireEvent("load", this, [], options, o);
14976 if(options.callback){
14977 options.callback.call(options.scope || this, [], options, false);
14981 // if data returned failure - throw an exception.
14982 if (o.success === false) {
14983 // show a message if no listener is registered.
14984 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14985 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14987 // loadmask wil be hooked into this..
14988 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14991 var r = o.records, t = o.totalRecords || r.length;
14993 this.fireEvent("beforeloadadd", this, r, options, o);
14995 if(!options || options.add !== true){
14996 if(this.pruneModifiedRecords){
14997 this.modified = [];
14999 for(var i = 0, len = r.length; i < len; i++){
15003 this.data = this.snapshot;
15004 delete this.snapshot;
15007 this.data.addAll(r);
15008 this.totalLength = t;
15010 this.fireEvent("datachanged", this);
15012 this.totalLength = Math.max(t, this.data.length+r.length);
15016 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15018 var e = new Roo.data.Record({});
15020 e.set(this.parent.displayField, this.parent.emptyTitle);
15021 e.set(this.parent.valueField, '');
15026 this.fireEvent("load", this, r, options, o);
15027 if(options.callback){
15028 options.callback.call(options.scope || this, r, options, true);
15034 * Loads data from a passed data block. A Reader which understands the format of the data
15035 * must have been configured in the constructor.
15036 * @param {Object} data The data block from which to read the Records. The format of the data expected
15037 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15038 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15040 loadData : function(o, append){
15041 var r = this.reader.readRecords(o);
15042 this.loadRecords(r, {add: append}, true);
15046 * using 'cn' the nested child reader read the child array into it's child stores.
15047 * @param {Object} rec The record with a 'children array
15049 loadDataFromChildren : function(rec)
15051 this.loadData(this.reader.toLoadData(rec));
15056 * Gets the number of cached records.
15058 * <em>If using paging, this may not be the total size of the dataset. If the data object
15059 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15060 * the data set size</em>
15062 getCount : function(){
15063 return this.data.length || 0;
15067 * Gets the total number of records in the dataset as returned by the server.
15069 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15070 * the dataset size</em>
15072 getTotalCount : function(){
15073 return this.totalLength || 0;
15077 * Returns the sort state of the Store as an object with two properties:
15079 field {String} The name of the field by which the Records are sorted
15080 direction {String} The sort order, "ASC" or "DESC"
15083 getSortState : function(){
15084 return this.sortInfo;
15088 applySort : function(){
15089 if(this.sortInfo && !this.remoteSort){
15090 var s = this.sortInfo, f = s.field;
15091 var st = this.fields.get(f).sortType;
15092 var fn = function(r1, r2){
15093 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15094 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15096 this.data.sort(s.direction, fn);
15097 if(this.snapshot && this.snapshot != this.data){
15098 this.snapshot.sort(s.direction, fn);
15104 * Sets the default sort column and order to be used by the next load operation.
15105 * @param {String} fieldName The name of the field to sort by.
15106 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15108 setDefaultSort : function(field, dir){
15109 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15113 * Sort the Records.
15114 * If remote sorting is used, the sort is performed on the server, and the cache is
15115 * reloaded. If local sorting is used, the cache is sorted internally.
15116 * @param {String} fieldName The name of the field to sort by.
15117 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15119 sort : function(fieldName, dir){
15120 var f = this.fields.get(fieldName);
15122 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15124 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15125 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15130 this.sortToggle[f.name] = dir;
15131 this.sortInfo = {field: f.name, direction: dir};
15132 if(!this.remoteSort){
15134 this.fireEvent("datachanged", this);
15136 this.load(this.lastOptions);
15141 * Calls the specified function for each of the Records in the cache.
15142 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15143 * Returning <em>false</em> aborts and exits the iteration.
15144 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15146 each : function(fn, scope){
15147 this.data.each(fn, scope);
15151 * Gets all records modified since the last commit. Modified records are persisted across load operations
15152 * (e.g., during paging).
15153 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15155 getModifiedRecords : function(){
15156 return this.modified;
15160 createFilterFn : function(property, value, anyMatch){
15161 if(!value.exec){ // not a regex
15162 value = String(value);
15163 if(value.length == 0){
15166 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15168 return function(r){
15169 return value.test(r.data[property]);
15174 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15175 * @param {String} property A field on your records
15176 * @param {Number} start The record index to start at (defaults to 0)
15177 * @param {Number} end The last record index to include (defaults to length - 1)
15178 * @return {Number} The sum
15180 sum : function(property, start, end){
15181 var rs = this.data.items, v = 0;
15182 start = start || 0;
15183 end = (end || end === 0) ? end : rs.length-1;
15185 for(var i = start; i <= end; i++){
15186 v += (rs[i].data[property] || 0);
15192 * Filter the records by a specified property.
15193 * @param {String} field A field on your records
15194 * @param {String/RegExp} value Either a string that the field
15195 * should start with or a RegExp to test against the field
15196 * @param {Boolean} anyMatch True to match any part not just the beginning
15198 filter : function(property, value, anyMatch){
15199 var fn = this.createFilterFn(property, value, anyMatch);
15200 return fn ? this.filterBy(fn) : this.clearFilter();
15204 * Filter by a function. The specified function will be called with each
15205 * record in this data source. If the function returns true the record is included,
15206 * otherwise it is filtered.
15207 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15208 * @param {Object} scope (optional) The scope of the function (defaults to this)
15210 filterBy : function(fn, scope){
15211 this.snapshot = this.snapshot || this.data;
15212 this.data = this.queryBy(fn, scope||this);
15213 this.fireEvent("datachanged", this);
15217 * Query the records by a specified property.
15218 * @param {String} field A field on your records
15219 * @param {String/RegExp} value Either a string that the field
15220 * should start with or a RegExp to test against the field
15221 * @param {Boolean} anyMatch True to match any part not just the beginning
15222 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15224 query : function(property, value, anyMatch){
15225 var fn = this.createFilterFn(property, value, anyMatch);
15226 return fn ? this.queryBy(fn) : this.data.clone();
15230 * Query by a function. The specified function will be called with each
15231 * record in this data source. If the function returns true the record is included
15233 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15234 * @param {Object} scope (optional) The scope of the function (defaults to this)
15235 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15237 queryBy : function(fn, scope){
15238 var data = this.snapshot || this.data;
15239 return data.filterBy(fn, scope||this);
15243 * Collects unique values for a particular dataIndex from this store.
15244 * @param {String} dataIndex The property to collect
15245 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15246 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15247 * @return {Array} An array of the unique values
15249 collect : function(dataIndex, allowNull, bypassFilter){
15250 var d = (bypassFilter === true && this.snapshot) ?
15251 this.snapshot.items : this.data.items;
15252 var v, sv, r = [], l = {};
15253 for(var i = 0, len = d.length; i < len; i++){
15254 v = d[i].data[dataIndex];
15256 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15265 * Revert to a view of the Record cache with no filtering applied.
15266 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15268 clearFilter : function(suppressEvent){
15269 if(this.snapshot && this.snapshot != this.data){
15270 this.data = this.snapshot;
15271 delete this.snapshot;
15272 if(suppressEvent !== true){
15273 this.fireEvent("datachanged", this);
15279 afterEdit : function(record){
15280 if(this.modified.indexOf(record) == -1){
15281 this.modified.push(record);
15283 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15287 afterReject : function(record){
15288 this.modified.remove(record);
15289 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15293 afterCommit : function(record){
15294 this.modified.remove(record);
15295 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15299 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15300 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15302 commitChanges : function(){
15303 var m = this.modified.slice(0);
15304 this.modified = [];
15305 for(var i = 0, len = m.length; i < len; i++){
15311 * Cancel outstanding changes on all changed records.
15313 rejectChanges : function(){
15314 var m = this.modified.slice(0);
15315 this.modified = [];
15316 for(var i = 0, len = m.length; i < len; i++){
15321 onMetaChange : function(meta, rtype, o){
15322 this.recordType = rtype;
15323 this.fields = rtype.prototype.fields;
15324 delete this.snapshot;
15325 this.sortInfo = meta.sortInfo || this.sortInfo;
15326 this.modified = [];
15327 this.fireEvent('metachange', this, this.reader.meta);
15330 moveIndex : function(data, type)
15332 var index = this.indexOf(data);
15334 var newIndex = index + type;
15338 this.insert(newIndex, data);
15343 * Ext JS Library 1.1.1
15344 * Copyright(c) 2006-2007, Ext JS, LLC.
15346 * Originally Released Under LGPL - original licence link has changed is not relivant.
15349 * <script type="text/javascript">
15353 * @class Roo.data.SimpleStore
15354 * @extends Roo.data.Store
15355 * Small helper class to make creating Stores from Array data easier.
15356 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15357 * @cfg {Array} fields An array of field definition objects, or field name strings.
15358 * @cfg {Object} an existing reader (eg. copied from another store)
15359 * @cfg {Array} data The multi-dimensional array of data
15360 * @cfg {Roo.data.DataProxy} proxy [not-required]
15361 * @cfg {Roo.data.Reader} reader [not-required]
15363 * @param {Object} config
15365 Roo.data.SimpleStore = function(config)
15367 Roo.data.SimpleStore.superclass.constructor.call(this, {
15369 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15372 Roo.data.Record.create(config.fields)
15374 proxy : new Roo.data.MemoryProxy(config.data)
15378 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15380 * Ext JS Library 1.1.1
15381 * Copyright(c) 2006-2007, Ext JS, LLC.
15383 * Originally Released Under LGPL - original licence link has changed is not relivant.
15386 * <script type="text/javascript">
15391 * @extends Roo.data.Store
15392 * @class Roo.data.JsonStore
15393 * Small helper class to make creating Stores for JSON data easier. <br/>
15395 var store = new Roo.data.JsonStore({
15396 url: 'get-images.php',
15398 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15401 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15402 * JsonReader and HttpProxy (unless inline data is provided).</b>
15403 * @cfg {Array} fields An array of field definition objects, or field name strings.
15405 * @param {Object} config
15407 Roo.data.JsonStore = function(c){
15408 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15409 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15410 reader: new Roo.data.JsonReader(c, c.fields)
15413 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15415 * Ext JS Library 1.1.1
15416 * Copyright(c) 2006-2007, Ext JS, LLC.
15418 * Originally Released Under LGPL - original licence link has changed is not relivant.
15421 * <script type="text/javascript">
15425 Roo.data.Field = function(config){
15426 if(typeof config == "string"){
15427 config = {name: config};
15429 Roo.apply(this, config);
15432 this.type = "auto";
15435 var st = Roo.data.SortTypes;
15436 // named sortTypes are supported, here we look them up
15437 if(typeof this.sortType == "string"){
15438 this.sortType = st[this.sortType];
15441 // set default sortType for strings and dates
15442 if(!this.sortType){
15445 this.sortType = st.asUCString;
15448 this.sortType = st.asDate;
15451 this.sortType = st.none;
15456 var stripRe = /[\$,%]/g;
15458 // prebuilt conversion function for this field, instead of
15459 // switching every time we're reading a value
15461 var cv, dateFormat = this.dateFormat;
15466 cv = function(v){ return v; };
15469 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15473 return v !== undefined && v !== null && v !== '' ?
15474 parseInt(String(v).replace(stripRe, ""), 10) : '';
15479 return v !== undefined && v !== null && v !== '' ?
15480 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15485 cv = function(v){ return v === true || v === "true" || v == 1; };
15492 if(v instanceof Date){
15496 if(dateFormat == "timestamp"){
15497 return new Date(v*1000);
15499 return Date.parseDate(v, dateFormat);
15501 var parsed = Date.parse(v);
15502 return parsed ? new Date(parsed) : null;
15511 Roo.data.Field.prototype = {
15519 * Ext JS Library 1.1.1
15520 * Copyright(c) 2006-2007, Ext JS, LLC.
15522 * Originally Released Under LGPL - original licence link has changed is not relivant.
15525 * <script type="text/javascript">
15528 // Base class for reading structured data from a data source. This class is intended to be
15529 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15532 * @class Roo.data.DataReader
15534 * Base class for reading structured data from a data source. This class is intended to be
15535 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15538 Roo.data.DataReader = function(meta, recordType){
15542 this.recordType = recordType instanceof Array ?
15543 Roo.data.Record.create(recordType) : recordType;
15546 Roo.data.DataReader.prototype = {
15549 readerType : 'Data',
15551 * Create an empty record
15552 * @param {Object} data (optional) - overlay some values
15553 * @return {Roo.data.Record} record created.
15555 newRow : function(d) {
15557 this.recordType.prototype.fields.each(function(c) {
15559 case 'int' : da[c.name] = 0; break;
15560 case 'date' : da[c.name] = new Date(); break;
15561 case 'float' : da[c.name] = 0.0; break;
15562 case 'boolean' : da[c.name] = false; break;
15563 default : da[c.name] = ""; break;
15567 return new this.recordType(Roo.apply(da, d));
15573 * Ext JS Library 1.1.1
15574 * Copyright(c) 2006-2007, Ext JS, LLC.
15576 * Originally Released Under LGPL - original licence link has changed is not relivant.
15579 * <script type="text/javascript">
15583 * @class Roo.data.DataProxy
15584 * @extends Roo.data.Observable
15586 * This class is an abstract base class for implementations which provide retrieval of
15587 * unformatted data objects.<br>
15589 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15590 * (of the appropriate type which knows how to parse the data object) to provide a block of
15591 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15593 * Custom implementations must implement the load method as described in
15594 * {@link Roo.data.HttpProxy#load}.
15596 Roo.data.DataProxy = function(){
15599 * @event beforeload
15600 * Fires before a network request is made to retrieve a data object.
15601 * @param {Object} This DataProxy object.
15602 * @param {Object} params The params parameter to the load function.
15607 * Fires before the load method's callback is called.
15608 * @param {Object} This DataProxy object.
15609 * @param {Object} o The data object.
15610 * @param {Object} arg The callback argument object passed to the load function.
15614 * @event loadexception
15615 * Fires if an Exception occurs during data retrieval.
15616 * @param {Object} This DataProxy object.
15617 * @param {Object} o The data object.
15618 * @param {Object} arg The callback argument object passed to the load function.
15619 * @param {Object} e The Exception.
15621 loadexception : true
15623 Roo.data.DataProxy.superclass.constructor.call(this);
15626 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15629 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15633 * Ext JS Library 1.1.1
15634 * Copyright(c) 2006-2007, Ext JS, LLC.
15636 * Originally Released Under LGPL - original licence link has changed is not relivant.
15639 * <script type="text/javascript">
15642 * @class Roo.data.MemoryProxy
15643 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15644 * to the Reader when its load method is called.
15646 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15648 Roo.data.MemoryProxy = function(data){
15652 Roo.data.MemoryProxy.superclass.constructor.call(this);
15656 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15659 * Load data from the requested source (in this case an in-memory
15660 * data object passed to the constructor), read the data object into
15661 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15662 * process that block using the passed callback.
15663 * @param {Object} params This parameter is not used by the MemoryProxy class.
15664 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15665 * object into a block of Roo.data.Records.
15666 * @param {Function} callback The function into which to pass the block of Roo.data.records.
15667 * The function must be passed <ul>
15668 * <li>The Record block object</li>
15669 * <li>The "arg" argument from the load function</li>
15670 * <li>A boolean success indicator</li>
15672 * @param {Object} scope The scope in which to call the callback
15673 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15675 load : function(params, reader, callback, scope, arg){
15676 params = params || {};
15679 result = reader.readRecords(params.data ? params.data :this.data);
15681 this.fireEvent("loadexception", this, arg, null, e);
15682 callback.call(scope, null, arg, false);
15685 callback.call(scope, result, arg, true);
15689 update : function(params, records){
15694 * Ext JS Library 1.1.1
15695 * Copyright(c) 2006-2007, Ext JS, LLC.
15697 * Originally Released Under LGPL - original licence link has changed is not relivant.
15700 * <script type="text/javascript">
15703 * @class Roo.data.HttpProxy
15704 * @extends Roo.data.DataProxy
15705 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15706 * configured to reference a certain URL.<br><br>
15708 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15709 * from which the running page was served.<br><br>
15711 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15713 * Be aware that to enable the browser to parse an XML document, the server must set
15714 * the Content-Type header in the HTTP response to "text/xml".
15716 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15717 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
15718 * will be used to make the request.
15720 Roo.data.HttpProxy = function(conn){
15721 Roo.data.HttpProxy.superclass.constructor.call(this);
15722 // is conn a conn config or a real conn?
15724 this.useAjax = !conn || !conn.events;
15728 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15729 // thse are take from connection...
15732 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15735 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15736 * extra parameters to each request made by this object. (defaults to undefined)
15739 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15740 * to each request made by this object. (defaults to undefined)
15743 * @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)
15746 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15749 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15755 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15759 * Return the {@link Roo.data.Connection} object being used by this Proxy.
15760 * @return {Connection} The Connection object. This object may be used to subscribe to events on
15761 * a finer-grained basis than the DataProxy events.
15763 getConnection : function(){
15764 return this.useAjax ? Roo.Ajax : this.conn;
15768 * Load data from the configured {@link Roo.data.Connection}, read the data object into
15769 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15770 * process that block using the passed callback.
15771 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15772 * for the request to the remote server.
15773 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15774 * object into a block of Roo.data.Records.
15775 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15776 * The function must be passed <ul>
15777 * <li>The Record block object</li>
15778 * <li>The "arg" argument from the load function</li>
15779 * <li>A boolean success indicator</li>
15781 * @param {Object} scope The scope in which to call the callback
15782 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15784 load : function(params, reader, callback, scope, arg){
15785 if(this.fireEvent("beforeload", this, params) !== false){
15787 params : params || {},
15789 callback : callback,
15794 callback : this.loadResponse,
15798 Roo.applyIf(o, this.conn);
15799 if(this.activeRequest){
15800 Roo.Ajax.abort(this.activeRequest);
15802 this.activeRequest = Roo.Ajax.request(o);
15804 this.conn.request(o);
15807 callback.call(scope||this, null, arg, false);
15812 loadResponse : function(o, success, response){
15813 delete this.activeRequest;
15815 this.fireEvent("loadexception", this, o, response);
15816 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15821 result = o.reader.read(response);
15823 this.fireEvent("loadexception", this, o, response, e);
15824 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15828 this.fireEvent("load", this, o, o.request.arg);
15829 o.request.callback.call(o.request.scope, result, o.request.arg, true);
15833 update : function(dataSet){
15838 updateResponse : function(dataSet){
15843 * Ext JS Library 1.1.1
15844 * Copyright(c) 2006-2007, Ext JS, LLC.
15846 * Originally Released Under LGPL - original licence link has changed is not relivant.
15849 * <script type="text/javascript">
15853 * @class Roo.data.ScriptTagProxy
15854 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15855 * other than the originating domain of the running page.<br><br>
15857 * <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
15858 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15860 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15861 * source code that is used as the source inside a <script> tag.<br><br>
15863 * In order for the browser to process the returned data, the server must wrap the data object
15864 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15865 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15866 * depending on whether the callback name was passed:
15869 boolean scriptTag = false;
15870 String cb = request.getParameter("callback");
15873 response.setContentType("text/javascript");
15875 response.setContentType("application/x-json");
15877 Writer out = response.getWriter();
15879 out.write(cb + "(");
15881 out.print(dataBlock.toJsonString());
15888 * @param {Object} config A configuration object.
15890 Roo.data.ScriptTagProxy = function(config){
15891 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15892 Roo.apply(this, config);
15893 this.head = document.getElementsByTagName("head")[0];
15896 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15898 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15900 * @cfg {String} url The URL from which to request the data object.
15903 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15907 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15908 * the server the name of the callback function set up by the load call to process the returned data object.
15909 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15910 * javascript output which calls this named function passing the data object as its only parameter.
15912 callbackParam : "callback",
15914 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15915 * name to the request.
15920 * Load data from the configured URL, read the data object into
15921 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15922 * process that block using the passed callback.
15923 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15924 * for the request to the remote server.
15925 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15926 * object into a block of Roo.data.Records.
15927 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15928 * The function must be passed <ul>
15929 * <li>The Record block object</li>
15930 * <li>The "arg" argument from the load function</li>
15931 * <li>A boolean success indicator</li>
15933 * @param {Object} scope The scope in which to call the callback
15934 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15936 load : function(params, reader, callback, scope, arg){
15937 if(this.fireEvent("beforeload", this, params) !== false){
15939 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15941 var url = this.url;
15942 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15944 url += "&_dc=" + (new Date().getTime());
15946 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15949 cb : "stcCallback"+transId,
15950 scriptId : "stcScript"+transId,
15954 callback : callback,
15960 window[trans.cb] = function(o){
15961 conn.handleResponse(o, trans);
15964 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15966 if(this.autoAbort !== false){
15970 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15972 var script = document.createElement("script");
15973 script.setAttribute("src", url);
15974 script.setAttribute("type", "text/javascript");
15975 script.setAttribute("id", trans.scriptId);
15976 this.head.appendChild(script);
15978 this.trans = trans;
15980 callback.call(scope||this, null, arg, false);
15985 isLoading : function(){
15986 return this.trans ? true : false;
15990 * Abort the current server request.
15992 abort : function(){
15993 if(this.isLoading()){
15994 this.destroyTrans(this.trans);
15999 destroyTrans : function(trans, isLoaded){
16000 this.head.removeChild(document.getElementById(trans.scriptId));
16001 clearTimeout(trans.timeoutId);
16003 window[trans.cb] = undefined;
16005 delete window[trans.cb];
16008 // if hasn't been loaded, wait for load to remove it to prevent script error
16009 window[trans.cb] = function(){
16010 window[trans.cb] = undefined;
16012 delete window[trans.cb];
16019 handleResponse : function(o, trans){
16020 this.trans = false;
16021 this.destroyTrans(trans, true);
16024 result = trans.reader.readRecords(o);
16026 this.fireEvent("loadexception", this, o, trans.arg, e);
16027 trans.callback.call(trans.scope||window, null, trans.arg, false);
16030 this.fireEvent("load", this, o, trans.arg);
16031 trans.callback.call(trans.scope||window, result, trans.arg, true);
16035 handleFailure : function(trans){
16036 this.trans = false;
16037 this.destroyTrans(trans, false);
16038 this.fireEvent("loadexception", this, null, trans.arg);
16039 trans.callback.call(trans.scope||window, null, trans.arg, false);
16043 * Ext JS Library 1.1.1
16044 * Copyright(c) 2006-2007, Ext JS, LLC.
16046 * Originally Released Under LGPL - original licence link has changed is not relivant.
16049 * <script type="text/javascript">
16053 * @class Roo.data.JsonReader
16054 * @extends Roo.data.DataReader
16055 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16056 * based on mappings in a provided Roo.data.Record constructor.
16058 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16059 * in the reply previously.
16064 var RecordDef = Roo.data.Record.create([
16065 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16066 {name: 'occupation'} // This field will use "occupation" as the mapping.
16068 var myReader = new Roo.data.JsonReader({
16069 totalProperty: "results", // The property which contains the total dataset size (optional)
16070 root: "rows", // The property which contains an Array of row objects
16071 id: "id" // The property within each row object that provides an ID for the record (optional)
16075 * This would consume a JSON file like this:
16077 { 'results': 2, 'rows': [
16078 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16079 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16082 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16083 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16084 * paged from the remote server.
16085 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16086 * @cfg {String} root name of the property which contains the Array of row objects.
16087 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16088 * @cfg {Array} fields Array of field definition objects
16090 * Create a new JsonReader
16091 * @param {Object} meta Metadata configuration options
16092 * @param {Object} recordType Either an Array of field definition objects,
16093 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16095 Roo.data.JsonReader = function(meta, recordType){
16098 // set some defaults:
16099 Roo.applyIf(meta, {
16100 totalProperty: 'total',
16101 successProperty : 'success',
16106 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16108 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16110 readerType : 'Json',
16113 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16114 * Used by Store query builder to append _requestMeta to params.
16117 metaFromRemote : false,
16119 * This method is only used by a DataProxy which has retrieved data from a remote server.
16120 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16121 * @return {Object} data A data block which is used by an Roo.data.Store object as
16122 * a cache of Roo.data.Records.
16124 read : function(response){
16125 var json = response.responseText;
16127 var o = /* eval:var:o */ eval("("+json+")");
16129 throw {message: "JsonReader.read: Json object not found"};
16135 this.metaFromRemote = true;
16136 this.meta = o.metaData;
16137 this.recordType = Roo.data.Record.create(o.metaData.fields);
16138 this.onMetaChange(this.meta, this.recordType, o);
16140 return this.readRecords(o);
16143 // private function a store will implement
16144 onMetaChange : function(meta, recordType, o){
16151 simpleAccess: function(obj, subsc) {
16158 getJsonAccessor: function(){
16160 return function(expr) {
16162 return(re.test(expr))
16163 ? new Function("obj", "return obj." + expr)
16168 return Roo.emptyFn;
16173 * Create a data block containing Roo.data.Records from an XML document.
16174 * @param {Object} o An object which contains an Array of row objects in the property specified
16175 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16176 * which contains the total size of the dataset.
16177 * @return {Object} data A data block which is used by an Roo.data.Store object as
16178 * a cache of Roo.data.Records.
16180 readRecords : function(o){
16182 * After any data loads, the raw JSON data is available for further custom processing.
16186 var s = this.meta, Record = this.recordType,
16187 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16189 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16191 if(s.totalProperty) {
16192 this.getTotal = this.getJsonAccessor(s.totalProperty);
16194 if(s.successProperty) {
16195 this.getSuccess = this.getJsonAccessor(s.successProperty);
16197 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16199 var g = this.getJsonAccessor(s.id);
16200 this.getId = function(rec) {
16202 return (r === undefined || r === "") ? null : r;
16205 this.getId = function(){return null;};
16208 for(var jj = 0; jj < fl; jj++){
16210 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16211 this.ef[jj] = this.getJsonAccessor(map);
16215 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16216 if(s.totalProperty){
16217 var vt = parseInt(this.getTotal(o), 10);
16222 if(s.successProperty){
16223 var vs = this.getSuccess(o);
16224 if(vs === false || vs === 'false'){
16229 for(var i = 0; i < c; i++){
16232 var id = this.getId(n);
16233 for(var j = 0; j < fl; j++){
16235 var v = this.ef[j](n);
16237 Roo.log('missing convert for ' + f.name);
16241 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16243 var record = new Record(values, id);
16245 records[i] = record;
16251 totalRecords : totalRecords
16254 // used when loading children.. @see loadDataFromChildren
16255 toLoadData: function(rec)
16257 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16258 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16259 return { data : data, total : data.length };
16264 * Ext JS Library 1.1.1
16265 * Copyright(c) 2006-2007, Ext JS, LLC.
16267 * Originally Released Under LGPL - original licence link has changed is not relivant.
16270 * <script type="text/javascript">
16274 * @class Roo.data.ArrayReader
16275 * @extends Roo.data.DataReader
16276 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16277 * Each element of that Array represents a row of data fields. The
16278 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16279 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16283 var RecordDef = Roo.data.Record.create([
16284 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16285 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16287 var myReader = new Roo.data.ArrayReader({
16288 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16292 * This would consume an Array like this:
16294 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16298 * Create a new JsonReader
16299 * @param {Object} meta Metadata configuration options.
16300 * @param {Object|Array} recordType Either an Array of field definition objects
16302 * @cfg {Array} fields Array of field definition objects
16303 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16304 * as specified to {@link Roo.data.Record#create},
16305 * or an {@link Roo.data.Record} object
16308 * created using {@link Roo.data.Record#create}.
16310 Roo.data.ArrayReader = function(meta, recordType)
16312 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16315 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16318 * Create a data block containing Roo.data.Records from an XML document.
16319 * @param {Object} o An Array of row objects which represents the dataset.
16320 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16321 * a cache of Roo.data.Records.
16323 readRecords : function(o)
16325 var sid = this.meta ? this.meta.id : null;
16326 var recordType = this.recordType, fields = recordType.prototype.fields;
16329 for(var i = 0; i < root.length; i++){
16332 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16333 for(var j = 0, jlen = fields.length; j < jlen; j++){
16334 var f = fields.items[j];
16335 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16336 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16338 values[f.name] = v;
16340 var record = new recordType(values, id);
16342 records[records.length] = record;
16346 totalRecords : records.length
16349 // used when loading children.. @see loadDataFromChildren
16350 toLoadData: function(rec)
16352 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16353 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16364 * @class Roo.bootstrap.ComboBox
16365 * @extends Roo.bootstrap.TriggerField
16366 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16367 * @cfg {Boolean} append (true|false) default false
16368 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16369 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16370 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16371 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16372 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16373 * @cfg {Boolean} animate default true
16374 * @cfg {Boolean} emptyResultText only for touch device
16375 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16376 * @cfg {String} emptyTitle default ''
16377 * @cfg {Number} width fixed with? experimental
16379 * Create a new ComboBox.
16380 * @param {Object} config Configuration options
16382 Roo.bootstrap.ComboBox = function(config){
16383 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16387 * Fires when the dropdown list is expanded
16388 * @param {Roo.bootstrap.ComboBox} combo This combo box
16393 * Fires when the dropdown list is collapsed
16394 * @param {Roo.bootstrap.ComboBox} combo This combo box
16398 * @event beforeselect
16399 * Fires before a list item is selected. Return false to cancel the selection.
16400 * @param {Roo.bootstrap.ComboBox} combo This combo box
16401 * @param {Roo.data.Record} record The data record returned from the underlying store
16402 * @param {Number} index The index of the selected item in the dropdown list
16404 'beforeselect' : true,
16407 * Fires when a list item is selected
16408 * @param {Roo.bootstrap.ComboBox} combo This combo box
16409 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16410 * @param {Number} index The index of the selected item in the dropdown list
16414 * @event beforequery
16415 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16416 * The event object passed has these properties:
16417 * @param {Roo.bootstrap.ComboBox} combo This combo box
16418 * @param {String} query The query
16419 * @param {Boolean} forceAll true to force "all" query
16420 * @param {Boolean} cancel true to cancel the query
16421 * @param {Object} e The query event object
16423 'beforequery': true,
16426 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16427 * @param {Roo.bootstrap.ComboBox} combo This combo box
16432 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16433 * @param {Roo.bootstrap.ComboBox} combo This combo box
16434 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16439 * Fires when the remove value from the combobox array
16440 * @param {Roo.bootstrap.ComboBox} combo This combo box
16444 * @event afterremove
16445 * Fires when the remove value from the combobox array
16446 * @param {Roo.bootstrap.ComboBox} combo This combo box
16448 'afterremove' : true,
16450 * @event specialfilter
16451 * Fires when specialfilter
16452 * @param {Roo.bootstrap.ComboBox} combo This combo box
16454 'specialfilter' : true,
16457 * Fires when tick the element
16458 * @param {Roo.bootstrap.ComboBox} combo This combo box
16462 * @event touchviewdisplay
16463 * Fires when touch view require special display (default is using displayField)
16464 * @param {Roo.bootstrap.ComboBox} combo This combo box
16465 * @param {Object} cfg set html .
16467 'touchviewdisplay' : true
16472 this.tickItems = [];
16474 this.selectedIndex = -1;
16475 if(this.mode == 'local'){
16476 if(config.queryDelay === undefined){
16477 this.queryDelay = 10;
16479 if(config.minChars === undefined){
16485 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16488 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16489 * rendering into an Roo.Editor, defaults to false)
16492 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16493 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16496 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16499 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16500 * the dropdown list (defaults to undefined, with no header element)
16504 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16508 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16510 listWidth: undefined,
16512 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16513 * mode = 'remote' or 'text' if mode = 'local')
16515 displayField: undefined,
16518 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16519 * mode = 'remote' or 'value' if mode = 'local').
16520 * Note: use of a valueField requires the user make a selection
16521 * in order for a value to be mapped.
16523 valueField: undefined,
16525 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16530 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16531 * field's data value (defaults to the underlying DOM element's name)
16533 hiddenName: undefined,
16535 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16539 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16541 selectedClass: 'active',
16544 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16548 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16549 * anchor positions (defaults to 'tl-bl')
16551 listAlign: 'tl-bl?',
16553 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16557 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16558 * query specified by the allQuery config option (defaults to 'query')
16560 triggerAction: 'query',
16562 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16563 * (defaults to 4, does not apply if editable = false)
16567 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16568 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16572 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16573 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16577 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16578 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16582 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16583 * when editable = true (defaults to false)
16585 selectOnFocus:false,
16587 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16589 queryParam: 'query',
16591 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
16592 * when mode = 'remote' (defaults to 'Loading...')
16594 loadingText: 'Loading...',
16596 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16600 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16604 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16605 * traditional select (defaults to true)
16609 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16613 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16617 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16618 * listWidth has a higher value)
16622 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16623 * allow the user to set arbitrary text into the field (defaults to false)
16625 forceSelection:false,
16627 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16628 * if typeAhead = true (defaults to 250)
16630 typeAheadDelay : 250,
16632 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16633 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16635 valueNotFoundText : undefined,
16637 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16639 blockFocus : false,
16642 * @cfg {Boolean} disableClear Disable showing of clear button.
16644 disableClear : false,
16646 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
16648 alwaysQuery : false,
16651 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
16656 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16658 invalidClass : "has-warning",
16661 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16663 validClass : "has-success",
16666 * @cfg {Boolean} specialFilter (true|false) special filter default false
16668 specialFilter : false,
16671 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16673 mobileTouchView : true,
16676 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16678 useNativeIOS : false,
16681 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16683 mobile_restrict_height : false,
16685 ios_options : false,
16697 btnPosition : 'right',
16698 triggerList : true,
16699 showToggleBtn : true,
16701 emptyResultText: 'Empty',
16702 triggerText : 'Select',
16706 // element that contains real text value.. (when hidden is used..)
16708 getAutoCreate : function()
16713 * Render classic select for iso
16716 if(Roo.isIOS && this.useNativeIOS){
16717 cfg = this.getAutoCreateNativeIOS();
16725 if(Roo.isTouch && this.mobileTouchView){
16726 cfg = this.getAutoCreateTouchView();
16733 if(!this.tickable){
16734 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16739 * ComboBox with tickable selections
16742 var align = this.labelAlign || this.parentLabelAlign();
16745 cls : 'form-group roo-combobox-tickable' //input-group
16748 var btn_text_select = '';
16749 var btn_text_done = '';
16750 var btn_text_cancel = '';
16752 if (this.btn_text_show) {
16753 btn_text_select = 'Select';
16754 btn_text_done = 'Done';
16755 btn_text_cancel = 'Cancel';
16760 cls : 'tickable-buttons',
16765 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16766 //html : this.triggerText
16767 html: btn_text_select
16773 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16775 html: btn_text_done
16781 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16783 html: btn_text_cancel
16789 buttons.cn.unshift({
16791 cls: 'roo-select2-search-field-input'
16797 Roo.each(buttons.cn, function(c){
16799 c.cls += ' btn-' + _this.size;
16802 if (_this.disabled) {
16809 style : 'display: contents',
16814 cls: 'form-hidden-field'
16818 cls: 'roo-select2-choices',
16822 cls: 'roo-select2-search-field',
16833 cls: 'roo-select2-container input-group roo-select2-container-multi',
16839 // cls: 'typeahead typeahead-long dropdown-menu',
16840 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
16845 if(this.hasFeedback && !this.allowBlank){
16849 cls: 'glyphicon form-control-feedback'
16852 combobox.cn.push(feedback);
16859 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16860 tooltip : 'This field is required'
16862 if (Roo.bootstrap.version == 4) {
16865 style : 'display:none'
16868 if (align ==='left' && this.fieldLabel.length) {
16870 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
16877 cls : 'control-label col-form-label',
16878 html : this.fieldLabel
16890 var labelCfg = cfg.cn[1];
16891 var contentCfg = cfg.cn[2];
16894 if(this.indicatorpos == 'right'){
16900 cls : 'control-label col-form-label',
16904 html : this.fieldLabel
16920 labelCfg = cfg.cn[0];
16921 contentCfg = cfg.cn[1];
16925 if(this.labelWidth > 12){
16926 labelCfg.style = "width: " + this.labelWidth + 'px';
16928 if(this.width * 1 > 0){
16929 contentCfg.style = "width: " + this.width + 'px';
16931 if(this.labelWidth < 13 && this.labelmd == 0){
16932 this.labelmd = this.labelWidth;
16935 if(this.labellg > 0){
16936 labelCfg.cls += ' col-lg-' + this.labellg;
16937 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16940 if(this.labelmd > 0){
16941 labelCfg.cls += ' col-md-' + this.labelmd;
16942 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16945 if(this.labelsm > 0){
16946 labelCfg.cls += ' col-sm-' + this.labelsm;
16947 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16950 if(this.labelxs > 0){
16951 labelCfg.cls += ' col-xs-' + this.labelxs;
16952 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16956 } else if ( this.fieldLabel.length) {
16957 // Roo.log(" label");
16962 //cls : 'input-group-addon',
16963 html : this.fieldLabel
16968 if(this.indicatorpos == 'right'){
16972 //cls : 'input-group-addon',
16973 html : this.fieldLabel
16983 // Roo.log(" no label && no align");
16990 ['xs','sm','md','lg'].map(function(size){
16991 if (settings[size]) {
16992 cfg.cls += ' col-' + size + '-' + settings[size];
17000 _initEventsCalled : false,
17003 initEvents: function()
17005 if (this._initEventsCalled) { // as we call render... prevent looping...
17008 this._initEventsCalled = true;
17011 throw "can not find store for combo";
17014 this.indicator = this.indicatorEl();
17016 this.store = Roo.factory(this.store, Roo.data);
17017 this.store.parent = this;
17019 // if we are building from html. then this element is so complex, that we can not really
17020 // use the rendered HTML.
17021 // so we have to trash and replace the previous code.
17022 if (Roo.XComponent.build_from_html) {
17023 // remove this element....
17024 var e = this.el.dom, k=0;
17025 while (e ) { e = e.previousSibling; ++k;}
17030 this.rendered = false;
17032 this.render(this.parent().getChildContainer(true), k);
17035 if(Roo.isIOS && this.useNativeIOS){
17036 this.initIOSView();
17044 if(Roo.isTouch && this.mobileTouchView){
17045 this.initTouchView();
17050 this.initTickableEvents();
17054 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17056 if(this.hiddenName){
17058 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17060 this.hiddenField.dom.value =
17061 this.hiddenValue !== undefined ? this.hiddenValue :
17062 this.value !== undefined ? this.value : '';
17064 // prevent input submission
17065 this.el.dom.removeAttribute('name');
17066 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17071 // this.el.dom.setAttribute('autocomplete', 'off');
17074 var cls = 'x-combo-list';
17076 //this.list = new Roo.Layer({
17077 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17083 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17084 _this.list.setWidth(lw);
17087 this.list.on('mouseover', this.onViewOver, this);
17088 this.list.on('mousemove', this.onViewMove, this);
17089 this.list.on('scroll', this.onViewScroll, this);
17092 this.list.swallowEvent('mousewheel');
17093 this.assetHeight = 0;
17096 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17097 this.assetHeight += this.header.getHeight();
17100 this.innerList = this.list.createChild({cls:cls+'-inner'});
17101 this.innerList.on('mouseover', this.onViewOver, this);
17102 this.innerList.on('mousemove', this.onViewMove, this);
17103 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17105 if(this.allowBlank && !this.pageSize && !this.disableClear){
17106 this.footer = this.list.createChild({cls:cls+'-ft'});
17107 this.pageTb = new Roo.Toolbar(this.footer);
17111 this.footer = this.list.createChild({cls:cls+'-ft'});
17112 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17113 {pageSize: this.pageSize});
17117 if (this.pageTb && this.allowBlank && !this.disableClear) {
17119 this.pageTb.add(new Roo.Toolbar.Fill(), {
17120 cls: 'x-btn-icon x-btn-clear',
17122 handler: function()
17125 _this.clearValue();
17126 _this.onSelect(false, -1);
17131 this.assetHeight += this.footer.getHeight();
17136 this.tpl = Roo.bootstrap.version == 4 ?
17137 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17138 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17141 this.view = new Roo.View(this.list, this.tpl, {
17142 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17144 //this.view.wrapEl.setDisplayed(false);
17145 this.view.on('click', this.onViewClick, this);
17148 this.store.on('beforeload', this.onBeforeLoad, this);
17149 this.store.on('load', this.onLoad, this);
17150 this.store.on('loadexception', this.onLoadException, this);
17152 if(this.resizable){
17153 this.resizer = new Roo.Resizable(this.list, {
17154 pinned:true, handles:'se'
17156 this.resizer.on('resize', function(r, w, h){
17157 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17158 this.listWidth = w;
17159 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17160 this.restrictHeight();
17162 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17165 if(!this.editable){
17166 this.editable = true;
17167 this.setEditable(false);
17172 if (typeof(this.events.add.listeners) != 'undefined') {
17174 this.addicon = this.wrap.createChild(
17175 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17177 this.addicon.on('click', function(e) {
17178 this.fireEvent('add', this);
17181 if (typeof(this.events.edit.listeners) != 'undefined') {
17183 this.editicon = this.wrap.createChild(
17184 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17185 if (this.addicon) {
17186 this.editicon.setStyle('margin-left', '40px');
17188 this.editicon.on('click', function(e) {
17190 // we fire even if inothing is selected..
17191 this.fireEvent('edit', this, this.lastData );
17197 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17198 "up" : function(e){
17199 this.inKeyMode = true;
17203 "down" : function(e){
17204 if(!this.isExpanded()){
17205 this.onTriggerClick();
17207 this.inKeyMode = true;
17212 "enter" : function(e){
17213 // this.onViewClick();
17217 if(this.fireEvent("specialkey", this, e)){
17218 this.onViewClick(false);
17224 "esc" : function(e){
17228 "tab" : function(e){
17231 if(this.fireEvent("specialkey", this, e)){
17232 this.onViewClick(false);
17240 doRelay : function(foo, bar, hname){
17241 if(hname == 'down' || this.scope.isExpanded()){
17242 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17251 this.queryDelay = Math.max(this.queryDelay || 10,
17252 this.mode == 'local' ? 10 : 250);
17255 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17257 if(this.typeAhead){
17258 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17260 if(this.editable !== false){
17261 this.inputEl().on("keyup", this.onKeyUp, this);
17263 if(this.forceSelection){
17264 this.inputEl().on('blur', this.doForce, this);
17268 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17269 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17273 initTickableEvents: function()
17277 if(this.hiddenName){
17279 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17281 this.hiddenField.dom.value =
17282 this.hiddenValue !== undefined ? this.hiddenValue :
17283 this.value !== undefined ? this.value : '';
17285 // prevent input submission
17286 this.el.dom.removeAttribute('name');
17287 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17292 // this.list = this.el.select('ul.dropdown-menu',true).first();
17294 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17295 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17296 if(this.triggerList){
17297 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17300 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17301 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17303 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17304 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17306 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17307 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17309 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17310 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17311 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17314 this.cancelBtn.hide();
17319 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17320 _this.list.setWidth(lw);
17323 this.list.on('mouseover', this.onViewOver, this);
17324 this.list.on('mousemove', this.onViewMove, this);
17326 this.list.on('scroll', this.onViewScroll, this);
17329 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17330 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17333 this.view = new Roo.View(this.list, this.tpl, {
17338 selectedClass: this.selectedClass
17341 //this.view.wrapEl.setDisplayed(false);
17342 this.view.on('click', this.onViewClick, this);
17346 this.store.on('beforeload', this.onBeforeLoad, this);
17347 this.store.on('load', this.onLoad, this);
17348 this.store.on('loadexception', this.onLoadException, this);
17351 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17352 "up" : function(e){
17353 this.inKeyMode = true;
17357 "down" : function(e){
17358 this.inKeyMode = true;
17362 "enter" : function(e){
17363 if(this.fireEvent("specialkey", this, e)){
17364 this.onViewClick(false);
17370 "esc" : function(e){
17371 this.onTickableFooterButtonClick(e, false, false);
17374 "tab" : function(e){
17375 this.fireEvent("specialkey", this, e);
17377 this.onTickableFooterButtonClick(e, false, false);
17384 doRelay : function(e, fn, key){
17385 if(this.scope.isExpanded()){
17386 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17395 this.queryDelay = Math.max(this.queryDelay || 10,
17396 this.mode == 'local' ? 10 : 250);
17399 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17401 if(this.typeAhead){
17402 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17405 if(this.editable !== false){
17406 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17409 this.indicator = this.indicatorEl();
17411 if(this.indicator){
17412 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17413 this.indicator.hide();
17418 onDestroy : function(){
17420 this.view.setStore(null);
17421 this.view.el.removeAllListeners();
17422 this.view.el.remove();
17423 this.view.purgeListeners();
17426 this.list.dom.innerHTML = '';
17430 this.store.un('beforeload', this.onBeforeLoad, this);
17431 this.store.un('load', this.onLoad, this);
17432 this.store.un('loadexception', this.onLoadException, this);
17434 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17438 fireKey : function(e){
17439 if(e.isNavKeyPress() && !this.list.isVisible()){
17440 this.fireEvent("specialkey", this, e);
17445 onResize: function(w, h)
17449 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17451 // if(typeof w != 'number'){
17452 // // we do not handle it!?!?
17455 // var tw = this.trigger.getWidth();
17456 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17457 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17459 // this.inputEl().setWidth( this.adjustWidth('input', x));
17461 // //this.trigger.setStyle('left', x+'px');
17463 // if(this.list && this.listWidth === undefined){
17464 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17465 // this.list.setWidth(lw);
17466 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17474 * Allow or prevent the user from directly editing the field text. If false is passed,
17475 * the user will only be able to select from the items defined in the dropdown list. This method
17476 * is the runtime equivalent of setting the 'editable' config option at config time.
17477 * @param {Boolean} value True to allow the user to directly edit the field text
17479 setEditable : function(value){
17480 if(value == this.editable){
17483 this.editable = value;
17485 this.inputEl().dom.setAttribute('readOnly', true);
17486 this.inputEl().on('mousedown', this.onTriggerClick, this);
17487 this.inputEl().addClass('x-combo-noedit');
17489 this.inputEl().dom.removeAttribute('readOnly');
17490 this.inputEl().un('mousedown', this.onTriggerClick, this);
17491 this.inputEl().removeClass('x-combo-noedit');
17497 onBeforeLoad : function(combo,opts){
17498 if(!this.hasFocus){
17502 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17504 this.restrictHeight();
17505 this.selectedIndex = -1;
17509 onLoad : function(){
17511 this.hasQuery = false;
17513 if(!this.hasFocus){
17517 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17518 this.loading.hide();
17521 if(this.store.getCount() > 0){
17524 this.restrictHeight();
17525 if(this.lastQuery == this.allQuery){
17526 if(this.editable && !this.tickable){
17527 this.inputEl().dom.select();
17531 !this.selectByValue(this.value, true) &&
17534 !this.store.lastOptions ||
17535 typeof(this.store.lastOptions.add) == 'undefined' ||
17536 this.store.lastOptions.add != true
17539 this.select(0, true);
17542 if(this.autoFocus){
17545 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17546 this.taTask.delay(this.typeAheadDelay);
17550 this.onEmptyResults();
17556 onLoadException : function()
17558 this.hasQuery = false;
17560 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17561 this.loading.hide();
17564 if(this.tickable && this.editable){
17569 // only causes errors at present
17570 //Roo.log(this.store.reader.jsonData);
17571 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17573 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17579 onTypeAhead : function(){
17580 if(this.store.getCount() > 0){
17581 var r = this.store.getAt(0);
17582 var newValue = r.data[this.displayField];
17583 var len = newValue.length;
17584 var selStart = this.getRawValue().length;
17586 if(selStart != len){
17587 this.setRawValue(newValue);
17588 this.selectText(selStart, newValue.length);
17594 onSelect : function(record, index){
17596 if(this.fireEvent('beforeselect', this, record, index) !== false){
17598 this.setFromData(index > -1 ? record.data : false);
17601 this.fireEvent('select', this, record, index);
17606 * Returns the currently selected field value or empty string if no value is set.
17607 * @return {String} value The selected value
17609 getValue : function()
17611 if(Roo.isIOS && this.useNativeIOS){
17612 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17616 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17619 if(this.valueField){
17620 return typeof this.value != 'undefined' ? this.value : '';
17622 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17626 getRawValue : function()
17628 if(Roo.isIOS && this.useNativeIOS){
17629 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17632 var v = this.inputEl().getValue();
17638 * Clears any text/value currently set in the field
17640 clearValue : function(){
17642 if(this.hiddenField){
17643 this.hiddenField.dom.value = '';
17646 this.setRawValue('');
17647 this.lastSelectionText = '';
17648 this.lastData = false;
17650 var close = this.closeTriggerEl();
17661 * Sets the specified value into the field. If the value finds a match, the corresponding record text
17662 * will be displayed in the field. If the value does not match the data value of an existing item,
17663 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17664 * Otherwise the field will be blank (although the value will still be set).
17665 * @param {String} value The value to match
17667 setValue : function(v)
17669 if(Roo.isIOS && this.useNativeIOS){
17670 this.setIOSValue(v);
17680 if(this.valueField){
17681 var r = this.findRecord(this.valueField, v);
17683 text = r.data[this.displayField];
17684 }else if(this.valueNotFoundText !== undefined){
17685 text = this.valueNotFoundText;
17688 this.lastSelectionText = text;
17689 if(this.hiddenField){
17690 this.hiddenField.dom.value = v;
17692 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17695 var close = this.closeTriggerEl();
17698 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17704 * @property {Object} the last set data for the element
17709 * Sets the value of the field based on a object which is related to the record format for the store.
17710 * @param {Object} value the value to set as. or false on reset?
17712 setFromData : function(o){
17719 var dv = ''; // display value
17720 var vv = ''; // value value..
17722 if (this.displayField) {
17723 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17725 // this is an error condition!!!
17726 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17729 if(this.valueField){
17730 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17733 var close = this.closeTriggerEl();
17736 if(dv.length || vv * 1 > 0){
17738 this.blockFocus=true;
17744 if(this.hiddenField){
17745 this.hiddenField.dom.value = vv;
17747 this.lastSelectionText = dv;
17748 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17752 // no hidden field.. - we store the value in 'value', but still display
17753 // display field!!!!
17754 this.lastSelectionText = dv;
17755 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17762 reset : function(){
17763 // overridden so that last data is reset..
17770 this.setValue(this.originalValue);
17771 //this.clearInvalid();
17772 this.lastData = false;
17774 this.view.clearSelections();
17780 findRecord : function(prop, value){
17782 if(this.store.getCount() > 0){
17783 this.store.each(function(r){
17784 if(r.data[prop] == value){
17794 getName: function()
17796 // returns hidden if it's set..
17797 if (!this.rendered) {return ''};
17798 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
17802 onViewMove : function(e, t){
17803 this.inKeyMode = false;
17807 onViewOver : function(e, t){
17808 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17811 var item = this.view.findItemFromChild(t);
17814 var index = this.view.indexOf(item);
17815 this.select(index, false);
17820 onViewClick : function(view, doFocus, el, e)
17822 var index = this.view.getSelectedIndexes()[0];
17824 var r = this.store.getAt(index);
17828 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17835 Roo.each(this.tickItems, function(v,k){
17837 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17839 _this.tickItems.splice(k, 1);
17841 if(typeof(e) == 'undefined' && view == false){
17842 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17854 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17855 this.tickItems.push(r.data);
17858 if(typeof(e) == 'undefined' && view == false){
17859 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17866 this.onSelect(r, index);
17868 if(doFocus !== false && !this.blockFocus){
17869 this.inputEl().focus();
17874 restrictHeight : function(){
17875 //this.innerList.dom.style.height = '';
17876 //var inner = this.innerList.dom;
17877 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17878 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17879 //this.list.beginUpdate();
17880 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17881 this.list.alignTo(this.inputEl(), this.listAlign);
17882 this.list.alignTo(this.inputEl(), this.listAlign);
17883 //this.list.endUpdate();
17887 onEmptyResults : function(){
17889 if(this.tickable && this.editable){
17890 this.hasFocus = false;
17891 this.restrictHeight();
17899 * Returns true if the dropdown list is expanded, else false.
17901 isExpanded : function(){
17902 return this.list.isVisible();
17906 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17907 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17908 * @param {String} value The data value of the item to select
17909 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17910 * selected item if it is not currently in view (defaults to true)
17911 * @return {Boolean} True if the value matched an item in the list, else false
17913 selectByValue : function(v, scrollIntoView){
17914 if(v !== undefined && v !== null){
17915 var r = this.findRecord(this.valueField || this.displayField, v);
17917 this.select(this.store.indexOf(r), scrollIntoView);
17925 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17926 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17927 * @param {Number} index The zero-based index of the list item to select
17928 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17929 * selected item if it is not currently in view (defaults to true)
17931 select : function(index, scrollIntoView){
17932 this.selectedIndex = index;
17933 this.view.select(index);
17934 if(scrollIntoView !== false){
17935 var el = this.view.getNode(index);
17937 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17940 this.list.scrollChildIntoView(el, false);
17946 selectNext : function(){
17947 var ct = this.store.getCount();
17949 if(this.selectedIndex == -1){
17951 }else if(this.selectedIndex < ct-1){
17952 this.select(this.selectedIndex+1);
17958 selectPrev : function(){
17959 var ct = this.store.getCount();
17961 if(this.selectedIndex == -1){
17963 }else if(this.selectedIndex != 0){
17964 this.select(this.selectedIndex-1);
17970 onKeyUp : function(e){
17971 if(this.editable !== false && !e.isSpecialKey()){
17972 this.lastKey = e.getKey();
17973 this.dqTask.delay(this.queryDelay);
17978 validateBlur : function(){
17979 return !this.list || !this.list.isVisible();
17983 initQuery : function(){
17985 var v = this.getRawValue();
17987 if(this.tickable && this.editable){
17988 v = this.tickableInputEl().getValue();
17995 doForce : function(){
17996 if(this.inputEl().dom.value.length > 0){
17997 this.inputEl().dom.value =
17998 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18004 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
18005 * query allowing the query action to be canceled if needed.
18006 * @param {String} query The SQL query to execute
18007 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18008 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18009 * saved in the current store (defaults to false)
18011 doQuery : function(q, forceAll){
18013 if(q === undefined || q === null){
18018 forceAll: forceAll,
18022 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18027 forceAll = qe.forceAll;
18028 if(forceAll === true || (q.length >= this.minChars)){
18030 this.hasQuery = true;
18032 if(this.lastQuery != q || this.alwaysQuery){
18033 this.lastQuery = q;
18034 if(this.mode == 'local'){
18035 this.selectedIndex = -1;
18037 this.store.clearFilter();
18040 if(this.specialFilter){
18041 this.fireEvent('specialfilter', this);
18046 this.store.filter(this.displayField, q);
18049 this.store.fireEvent("datachanged", this.store);
18056 this.store.baseParams[this.queryParam] = q;
18058 var options = {params : this.getParams(q)};
18061 options.add = true;
18062 options.params.start = this.page * this.pageSize;
18065 this.store.load(options);
18068 * this code will make the page width larger, at the beginning, the list not align correctly,
18069 * we should expand the list on onLoad
18070 * so command out it
18075 this.selectedIndex = -1;
18080 this.loadNext = false;
18084 getParams : function(q){
18086 //p[this.queryParam] = q;
18090 p.limit = this.pageSize;
18096 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18098 collapse : function(){
18099 if(!this.isExpanded()){
18105 this.hasFocus = false;
18109 this.cancelBtn.hide();
18110 this.trigger.show();
18113 this.tickableInputEl().dom.value = '';
18114 this.tickableInputEl().blur();
18119 Roo.get(document).un('mousedown', this.collapseIf, this);
18120 Roo.get(document).un('mousewheel', this.collapseIf, this);
18121 if (!this.editable) {
18122 Roo.get(document).un('keydown', this.listKeyPress, this);
18124 this.fireEvent('collapse', this);
18130 collapseIf : function(e){
18131 var in_combo = e.within(this.el);
18132 var in_list = e.within(this.list);
18133 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18135 if (in_combo || in_list || is_list) {
18136 //e.stopPropagation();
18141 this.onTickableFooterButtonClick(e, false, false);
18149 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18151 expand : function(){
18153 if(this.isExpanded() || !this.hasFocus){
18157 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18158 this.list.setWidth(lw);
18164 this.restrictHeight();
18168 this.tickItems = Roo.apply([], this.item);
18171 this.cancelBtn.show();
18172 this.trigger.hide();
18175 this.tickableInputEl().focus();
18180 Roo.get(document).on('mousedown', this.collapseIf, this);
18181 Roo.get(document).on('mousewheel', this.collapseIf, this);
18182 if (!this.editable) {
18183 Roo.get(document).on('keydown', this.listKeyPress, this);
18186 this.fireEvent('expand', this);
18190 // Implements the default empty TriggerField.onTriggerClick function
18191 onTriggerClick : function(e)
18193 Roo.log('trigger click');
18195 if(this.disabled || !this.triggerList){
18200 this.loadNext = false;
18202 if(this.isExpanded()){
18204 if (!this.blockFocus) {
18205 this.inputEl().focus();
18209 this.hasFocus = true;
18210 if(this.triggerAction == 'all') {
18211 this.doQuery(this.allQuery, true);
18213 this.doQuery(this.getRawValue());
18215 if (!this.blockFocus) {
18216 this.inputEl().focus();
18221 onTickableTriggerClick : function(e)
18228 this.loadNext = false;
18229 this.hasFocus = true;
18231 if(this.triggerAction == 'all') {
18232 this.doQuery(this.allQuery, true);
18234 this.doQuery(this.getRawValue());
18238 onSearchFieldClick : function(e)
18240 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18241 this.onTickableFooterButtonClick(e, false, false);
18245 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18250 this.loadNext = false;
18251 this.hasFocus = true;
18253 if(this.triggerAction == 'all') {
18254 this.doQuery(this.allQuery, true);
18256 this.doQuery(this.getRawValue());
18260 listKeyPress : function(e)
18262 //Roo.log('listkeypress');
18263 // scroll to first matching element based on key pres..
18264 if (e.isSpecialKey()) {
18267 var k = String.fromCharCode(e.getKey()).toUpperCase();
18270 var csel = this.view.getSelectedNodes();
18271 var cselitem = false;
18273 var ix = this.view.indexOf(csel[0]);
18274 cselitem = this.store.getAt(ix);
18275 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18281 this.store.each(function(v) {
18283 // start at existing selection.
18284 if (cselitem.id == v.id) {
18290 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18291 match = this.store.indexOf(v);
18297 if (match === false) {
18298 return true; // no more action?
18301 this.view.select(match);
18302 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18303 sn.scrollIntoView(sn.dom.parentNode, false);
18306 onViewScroll : function(e, t){
18308 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){
18312 this.hasQuery = true;
18314 this.loading = this.list.select('.loading', true).first();
18316 if(this.loading === null){
18317 this.list.createChild({
18319 cls: 'loading roo-select2-more-results roo-select2-active',
18320 html: 'Loading more results...'
18323 this.loading = this.list.select('.loading', true).first();
18325 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18327 this.loading.hide();
18330 this.loading.show();
18335 this.loadNext = true;
18337 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18342 addItem : function(o)
18344 var dv = ''; // display value
18346 if (this.displayField) {
18347 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18349 // this is an error condition!!!
18350 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18357 var choice = this.choices.createChild({
18359 cls: 'roo-select2-search-choice',
18368 cls: 'roo-select2-search-choice-close fa fa-times',
18373 }, this.searchField);
18375 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18377 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18385 this.inputEl().dom.value = '';
18390 onRemoveItem : function(e, _self, o)
18392 e.preventDefault();
18394 this.lastItem = Roo.apply([], this.item);
18396 var index = this.item.indexOf(o.data) * 1;
18399 Roo.log('not this item?!');
18403 this.item.splice(index, 1);
18408 this.fireEvent('remove', this, e);
18414 syncValue : function()
18416 if(!this.item.length){
18423 Roo.each(this.item, function(i){
18424 if(_this.valueField){
18425 value.push(i[_this.valueField]);
18432 this.value = value.join(',');
18434 if(this.hiddenField){
18435 this.hiddenField.dom.value = this.value;
18438 this.store.fireEvent("datachanged", this.store);
18443 clearItem : function()
18445 if(!this.multiple){
18451 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18459 if(this.tickable && !Roo.isTouch){
18460 this.view.refresh();
18464 inputEl: function ()
18466 if(Roo.isIOS && this.useNativeIOS){
18467 return this.el.select('select.roo-ios-select', true).first();
18470 if(Roo.isTouch && this.mobileTouchView){
18471 return this.el.select('input.form-control',true).first();
18475 return this.searchField;
18478 return this.el.select('input.form-control',true).first();
18481 onTickableFooterButtonClick : function(e, btn, el)
18483 e.preventDefault();
18485 this.lastItem = Roo.apply([], this.item);
18487 if(btn && btn.name == 'cancel'){
18488 this.tickItems = Roo.apply([], this.item);
18497 Roo.each(this.tickItems, function(o){
18505 validate : function()
18507 if(this.getVisibilityEl().hasClass('hidden')){
18511 var v = this.getRawValue();
18514 v = this.getValue();
18517 if(this.disabled || this.allowBlank || v.length){
18522 this.markInvalid();
18526 tickableInputEl : function()
18528 if(!this.tickable || !this.editable){
18529 return this.inputEl();
18532 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18536 getAutoCreateTouchView : function()
18541 cls: 'form-group' //input-group
18547 type : this.inputType,
18548 cls : 'form-control x-combo-noedit',
18549 autocomplete: 'new-password',
18550 placeholder : this.placeholder || '',
18555 input.name = this.name;
18559 input.cls += ' input-' + this.size;
18562 if (this.disabled) {
18563 input.disabled = true;
18567 cls : 'roo-combobox-wrap',
18574 inputblock.cls += ' input-group';
18576 inputblock.cn.unshift({
18578 cls : 'input-group-addon input-group-prepend input-group-text',
18583 if(this.removable && !this.multiple){
18584 inputblock.cls += ' roo-removable';
18586 inputblock.cn.push({
18589 cls : 'roo-combo-removable-btn close'
18593 if(this.hasFeedback && !this.allowBlank){
18595 inputblock.cls += ' has-feedback';
18597 inputblock.cn.push({
18599 cls: 'glyphicon form-control-feedback'
18606 inputblock.cls += (this.before) ? '' : ' input-group';
18608 inputblock.cn.push({
18610 cls : 'input-group-addon input-group-append input-group-text',
18616 var ibwrap = inputblock;
18621 cls: 'roo-select2-choices',
18625 cls: 'roo-select2-search-field',
18638 cls: 'roo-select2-container input-group roo-touchview-combobox ',
18643 cls: 'form-hidden-field'
18649 if(!this.multiple && this.showToggleBtn){
18655 if (this.caret != false) {
18658 cls: 'fa fa-' + this.caret
18665 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18667 Roo.bootstrap.version == 3 ? caret : '',
18670 cls: 'combobox-clear',
18684 combobox.cls += ' roo-select2-container-multi';
18687 var required = this.allowBlank ? {
18689 style: 'display: none'
18692 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18693 tooltip : 'This field is required'
18696 var align = this.labelAlign || this.parentLabelAlign();
18698 if (align ==='left' && this.fieldLabel.length) {
18704 cls : 'control-label col-form-label',
18705 html : this.fieldLabel
18709 cls : 'roo-combobox-wrap ',
18716 var labelCfg = cfg.cn[1];
18717 var contentCfg = cfg.cn[2];
18720 if(this.indicatorpos == 'right'){
18725 cls : 'control-label col-form-label',
18729 html : this.fieldLabel
18735 cls : "roo-combobox-wrap ",
18743 labelCfg = cfg.cn[0];
18744 contentCfg = cfg.cn[1];
18749 if(this.labelWidth > 12){
18750 labelCfg.style = "width: " + this.labelWidth + 'px';
18753 if(this.labelWidth < 13 && this.labelmd == 0){
18754 this.labelmd = this.labelWidth;
18757 if(this.labellg > 0){
18758 labelCfg.cls += ' col-lg-' + this.labellg;
18759 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18762 if(this.labelmd > 0){
18763 labelCfg.cls += ' col-md-' + this.labelmd;
18764 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18767 if(this.labelsm > 0){
18768 labelCfg.cls += ' col-sm-' + this.labelsm;
18769 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18772 if(this.labelxs > 0){
18773 labelCfg.cls += ' col-xs-' + this.labelxs;
18774 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18778 } else if ( this.fieldLabel.length) {
18783 cls : 'control-label',
18784 html : this.fieldLabel
18795 if(this.indicatorpos == 'right'){
18799 cls : 'control-label',
18800 html : this.fieldLabel,
18818 var settings = this;
18820 ['xs','sm','md','lg'].map(function(size){
18821 if (settings[size]) {
18822 cfg.cls += ' col-' + size + '-' + settings[size];
18829 initTouchView : function()
18831 this.renderTouchView();
18833 this.touchViewEl.on('scroll', function(){
18834 this.el.dom.scrollTop = 0;
18837 this.originalValue = this.getValue();
18839 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18841 this.inputEl().on("click", this.showTouchView, this);
18842 if (this.triggerEl) {
18843 this.triggerEl.on("click", this.showTouchView, this);
18847 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18848 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18850 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18852 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18853 this.store.on('load', this.onTouchViewLoad, this);
18854 this.store.on('loadexception', this.onTouchViewLoadException, this);
18856 if(this.hiddenName){
18858 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18860 this.hiddenField.dom.value =
18861 this.hiddenValue !== undefined ? this.hiddenValue :
18862 this.value !== undefined ? this.value : '';
18864 this.el.dom.removeAttribute('name');
18865 this.hiddenField.dom.setAttribute('name', this.hiddenName);
18869 this.choices = this.el.select('ul.roo-select2-choices', true).first();
18870 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18873 if(this.removable && !this.multiple){
18874 var close = this.closeTriggerEl();
18876 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18877 close.on('click', this.removeBtnClick, this, close);
18881 * fix the bug in Safari iOS8
18883 this.inputEl().on("focus", function(e){
18884 document.activeElement.blur();
18887 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18894 renderTouchView : function()
18896 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18897 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18899 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18900 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18902 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18903 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18904 this.touchViewBodyEl.setStyle('overflow', 'auto');
18906 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18907 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18909 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18910 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18914 showTouchView : function()
18920 this.touchViewHeaderEl.hide();
18922 if(this.modalTitle.length){
18923 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18924 this.touchViewHeaderEl.show();
18927 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18928 this.touchViewEl.show();
18930 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18932 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18933 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18935 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18937 if(this.modalTitle.length){
18938 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18941 this.touchViewBodyEl.setHeight(bodyHeight);
18945 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18947 this.touchViewEl.addClass(['in','show']);
18950 if(this._touchViewMask){
18951 Roo.get(document.body).addClass("x-body-masked");
18952 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18953 this._touchViewMask.setStyle('z-index', 10000);
18954 this._touchViewMask.addClass('show');
18957 this.doTouchViewQuery();
18961 hideTouchView : function()
18963 this.touchViewEl.removeClass(['in','show']);
18967 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18969 this.touchViewEl.setStyle('display', 'none');
18972 if(this._touchViewMask){
18973 this._touchViewMask.removeClass('show');
18974 Roo.get(document.body).removeClass("x-body-masked");
18978 setTouchViewValue : function()
18985 Roo.each(this.tickItems, function(o){
18990 this.hideTouchView();
18993 doTouchViewQuery : function()
19002 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19006 if(!this.alwaysQuery || this.mode == 'local'){
19007 this.onTouchViewLoad();
19014 onTouchViewBeforeLoad : function(combo,opts)
19020 onTouchViewLoad : function()
19022 if(this.store.getCount() < 1){
19023 this.onTouchViewEmptyResults();
19027 this.clearTouchView();
19029 var rawValue = this.getRawValue();
19031 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19033 this.tickItems = [];
19035 this.store.data.each(function(d, rowIndex){
19036 var row = this.touchViewListGroup.createChild(template);
19038 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19039 row.addClass(d.data.cls);
19042 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19045 html : d.data[this.displayField]
19048 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19049 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19052 row.removeClass('selected');
19053 if(!this.multiple && this.valueField &&
19054 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19057 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19058 row.addClass('selected');
19061 if(this.multiple && this.valueField &&
19062 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19066 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19067 this.tickItems.push(d.data);
19070 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19074 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19076 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19078 if(this.modalTitle.length){
19079 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19082 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19084 if(this.mobile_restrict_height && listHeight < bodyHeight){
19085 this.touchViewBodyEl.setHeight(listHeight);
19090 if(firstChecked && listHeight > bodyHeight){
19091 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19096 onTouchViewLoadException : function()
19098 this.hideTouchView();
19101 onTouchViewEmptyResults : function()
19103 this.clearTouchView();
19105 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19107 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19111 clearTouchView : function()
19113 this.touchViewListGroup.dom.innerHTML = '';
19116 onTouchViewClick : function(e, el, o)
19118 e.preventDefault();
19121 var rowIndex = o.rowIndex;
19123 var r = this.store.getAt(rowIndex);
19125 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19127 if(!this.multiple){
19128 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19129 c.dom.removeAttribute('checked');
19132 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19134 this.setFromData(r.data);
19136 var close = this.closeTriggerEl();
19142 this.hideTouchView();
19144 this.fireEvent('select', this, r, rowIndex);
19149 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19150 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19151 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19155 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19156 this.addItem(r.data);
19157 this.tickItems.push(r.data);
19161 getAutoCreateNativeIOS : function()
19164 cls: 'form-group' //input-group,
19169 cls : 'roo-ios-select'
19173 combobox.name = this.name;
19176 if (this.disabled) {
19177 combobox.disabled = true;
19180 var settings = this;
19182 ['xs','sm','md','lg'].map(function(size){
19183 if (settings[size]) {
19184 cfg.cls += ' col-' + size + '-' + settings[size];
19194 initIOSView : function()
19196 this.store.on('load', this.onIOSViewLoad, this);
19201 onIOSViewLoad : function()
19203 if(this.store.getCount() < 1){
19207 this.clearIOSView();
19209 if(this.allowBlank) {
19211 var default_text = '-- SELECT --';
19213 if(this.placeholder.length){
19214 default_text = this.placeholder;
19217 if(this.emptyTitle.length){
19218 default_text += ' - ' + this.emptyTitle + ' -';
19221 var opt = this.inputEl().createChild({
19224 html : default_text
19228 o[this.valueField] = 0;
19229 o[this.displayField] = default_text;
19231 this.ios_options.push({
19238 this.store.data.each(function(d, rowIndex){
19242 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19243 html = d.data[this.displayField];
19248 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19249 value = d.data[this.valueField];
19258 if(this.value == d.data[this.valueField]){
19259 option['selected'] = true;
19262 var opt = this.inputEl().createChild(option);
19264 this.ios_options.push({
19271 this.inputEl().on('change', function(){
19272 this.fireEvent('select', this);
19277 clearIOSView: function()
19279 this.inputEl().dom.innerHTML = '';
19281 this.ios_options = [];
19284 setIOSValue: function(v)
19288 if(!this.ios_options){
19292 Roo.each(this.ios_options, function(opts){
19294 opts.el.dom.removeAttribute('selected');
19296 if(opts.data[this.valueField] != v){
19300 opts.el.dom.setAttribute('selected', true);
19306 * @cfg {Boolean} grow
19310 * @cfg {Number} growMin
19314 * @cfg {Number} growMax
19323 Roo.apply(Roo.bootstrap.ComboBox, {
19327 cls: 'modal-header',
19349 cls: 'list-group-item',
19353 cls: 'roo-combobox-list-group-item-value'
19357 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19371 listItemCheckbox : {
19373 cls: 'list-group-item',
19377 cls: 'roo-combobox-list-group-item-value'
19381 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19397 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19402 cls: 'modal-footer',
19410 cls: 'col-xs-6 text-left',
19413 cls: 'btn btn-danger roo-touch-view-cancel',
19419 cls: 'col-xs-6 text-right',
19422 cls: 'btn btn-success roo-touch-view-ok',
19433 Roo.apply(Roo.bootstrap.ComboBox, {
19435 touchViewTemplate : {
19437 cls: 'modal fade roo-combobox-touch-view',
19441 cls: 'modal-dialog',
19442 style : 'position:fixed', // we have to fix position....
19446 cls: 'modal-content',
19448 Roo.bootstrap.ComboBox.header,
19449 Roo.bootstrap.ComboBox.body,
19450 Roo.bootstrap.ComboBox.footer
19459 * Ext JS Library 1.1.1
19460 * Copyright(c) 2006-2007, Ext JS, LLC.
19462 * Originally Released Under LGPL - original licence link has changed is not relivant.
19465 * <script type="text/javascript">
19470 * @extends Roo.util.Observable
19471 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19472 * This class also supports single and multi selection modes. <br>
19473 * Create a data model bound view:
19475 var store = new Roo.data.Store(...);
19477 var view = new Roo.View({
19479 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19481 singleSelect: true,
19482 selectedClass: "ydataview-selected",
19486 // listen for node click?
19487 view.on("click", function(vw, index, node, e){
19488 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19492 dataModel.load("foobar.xml");
19494 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19496 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19497 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19499 * Note: old style constructor is still suported (container, template, config)
19502 * Create a new View
19503 * @param {Object} config The config object
19506 Roo.View = function(config, depreciated_tpl, depreciated_config){
19508 this.parent = false;
19510 if (typeof(depreciated_tpl) == 'undefined') {
19511 // new way.. - universal constructor.
19512 Roo.apply(this, config);
19513 this.el = Roo.get(this.el);
19516 this.el = Roo.get(config);
19517 this.tpl = depreciated_tpl;
19518 Roo.apply(this, depreciated_config);
19520 this.wrapEl = this.el.wrap().wrap();
19521 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19524 if(typeof(this.tpl) == "string"){
19525 this.tpl = new Roo.Template(this.tpl);
19527 // support xtype ctors..
19528 this.tpl = new Roo.factory(this.tpl, Roo);
19532 this.tpl.compile();
19537 * @event beforeclick
19538 * Fires before a click is processed. Returns false to cancel the default action.
19539 * @param {Roo.View} this
19540 * @param {Number} index The index of the target node
19541 * @param {HTMLElement} node The target node
19542 * @param {Roo.EventObject} e The raw event object
19544 "beforeclick" : true,
19547 * Fires when a template node is clicked.
19548 * @param {Roo.View} this
19549 * @param {Number} index The index of the target node
19550 * @param {HTMLElement} node The target node
19551 * @param {Roo.EventObject} e The raw event object
19556 * Fires when a template node is double clicked.
19557 * @param {Roo.View} this
19558 * @param {Number} index The index of the target node
19559 * @param {HTMLElement} node The target node
19560 * @param {Roo.EventObject} e The raw event object
19564 * @event contextmenu
19565 * Fires when a template node is right clicked.
19566 * @param {Roo.View} this
19567 * @param {Number} index The index of the target node
19568 * @param {HTMLElement} node The target node
19569 * @param {Roo.EventObject} e The raw event object
19571 "contextmenu" : true,
19573 * @event selectionchange
19574 * Fires when the selected nodes change.
19575 * @param {Roo.View} this
19576 * @param {Array} selections Array of the selected nodes
19578 "selectionchange" : true,
19581 * @event beforeselect
19582 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19583 * @param {Roo.View} this
19584 * @param {HTMLElement} node The node to be selected
19585 * @param {Array} selections Array of currently selected nodes
19587 "beforeselect" : true,
19589 * @event preparedata
19590 * Fires on every row to render, to allow you to change the data.
19591 * @param {Roo.View} this
19592 * @param {Object} data to be rendered (change this)
19594 "preparedata" : true
19602 "click": this.onClick,
19603 "dblclick": this.onDblClick,
19604 "contextmenu": this.onContextMenu,
19608 this.selections = [];
19610 this.cmp = new Roo.CompositeElementLite([]);
19612 this.store = Roo.factory(this.store, Roo.data);
19613 this.setStore(this.store, true);
19616 if ( this.footer && this.footer.xtype) {
19618 var fctr = this.wrapEl.appendChild(document.createElement("div"));
19620 this.footer.dataSource = this.store;
19621 this.footer.container = fctr;
19622 this.footer = Roo.factory(this.footer, Roo);
19623 fctr.insertFirst(this.el);
19625 // this is a bit insane - as the paging toolbar seems to detach the el..
19626 // dom.parentNode.parentNode.parentNode
19627 // they get detached?
19631 Roo.View.superclass.constructor.call(this);
19636 Roo.extend(Roo.View, Roo.util.Observable, {
19639 * @cfg {Roo.data.Store} store Data store to load data from.
19644 * @cfg {String|Roo.Element} el The container element.
19649 * @cfg {String|Roo.Template} tpl The template used by this View
19653 * @cfg {String} dataName the named area of the template to use as the data area
19654 * Works with domtemplates roo-name="name"
19658 * @cfg {String} selectedClass The css class to add to selected nodes
19660 selectedClass : "x-view-selected",
19662 * @cfg {String} emptyText The empty text to show when nothing is loaded.
19667 * @cfg {String} text to display on mask (default Loading)
19671 * @cfg {Boolean} multiSelect Allow multiple selection
19673 multiSelect : false,
19675 * @cfg {Boolean} singleSelect Allow single selection
19677 singleSelect: false,
19680 * @cfg {Boolean} toggleSelect - selecting
19682 toggleSelect : false,
19685 * @cfg {Boolean} tickable - selecting
19690 * Returns the element this view is bound to.
19691 * @return {Roo.Element}
19693 getEl : function(){
19694 return this.wrapEl;
19700 * Refreshes the view. - called by datachanged on the store. - do not call directly.
19702 refresh : function(){
19703 //Roo.log('refresh');
19706 // if we are using something like 'domtemplate', then
19707 // the what gets used is:
19708 // t.applySubtemplate(NAME, data, wrapping data..)
19709 // the outer template then get' applied with
19710 // the store 'extra data'
19711 // and the body get's added to the
19712 // roo-name="data" node?
19713 // <span class='roo-tpl-{name}'></span> ?????
19717 this.clearSelections();
19718 this.el.update("");
19720 var records = this.store.getRange();
19721 if(records.length < 1) {
19723 // is this valid?? = should it render a template??
19725 this.el.update(this.emptyText);
19729 if (this.dataName) {
19730 this.el.update(t.apply(this.store.meta)); //????
19731 el = this.el.child('.roo-tpl-' + this.dataName);
19734 for(var i = 0, len = records.length; i < len; i++){
19735 var data = this.prepareData(records[i].data, i, records[i]);
19736 this.fireEvent("preparedata", this, data, i, records[i]);
19738 var d = Roo.apply({}, data);
19741 Roo.apply(d, {'roo-id' : Roo.id()});
19745 Roo.each(this.parent.item, function(item){
19746 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19749 Roo.apply(d, {'roo-data-checked' : 'checked'});
19753 html[html.length] = Roo.util.Format.trim(
19755 t.applySubtemplate(this.dataName, d, this.store.meta) :
19762 el.update(html.join(""));
19763 this.nodes = el.dom.childNodes;
19764 this.updateIndexes(0);
19769 * Function to override to reformat the data that is sent to
19770 * the template for each node.
19771 * DEPRICATED - use the preparedata event handler.
19772 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19773 * a JSON object for an UpdateManager bound view).
19775 prepareData : function(data, index, record)
19777 this.fireEvent("preparedata", this, data, index, record);
19781 onUpdate : function(ds, record){
19782 // Roo.log('on update');
19783 this.clearSelections();
19784 var index = this.store.indexOf(record);
19785 var n = this.nodes[index];
19786 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19787 n.parentNode.removeChild(n);
19788 this.updateIndexes(index, index);
19794 onAdd : function(ds, records, index)
19796 //Roo.log(['on Add', ds, records, index] );
19797 this.clearSelections();
19798 if(this.nodes.length == 0){
19802 var n = this.nodes[index];
19803 for(var i = 0, len = records.length; i < len; i++){
19804 var d = this.prepareData(records[i].data, i, records[i]);
19806 this.tpl.insertBefore(n, d);
19809 this.tpl.append(this.el, d);
19812 this.updateIndexes(index);
19815 onRemove : function(ds, record, index){
19816 // Roo.log('onRemove');
19817 this.clearSelections();
19818 var el = this.dataName ?
19819 this.el.child('.roo-tpl-' + this.dataName) :
19822 el.dom.removeChild(this.nodes[index]);
19823 this.updateIndexes(index);
19827 * Refresh an individual node.
19828 * @param {Number} index
19830 refreshNode : function(index){
19831 this.onUpdate(this.store, this.store.getAt(index));
19834 updateIndexes : function(startIndex, endIndex){
19835 var ns = this.nodes;
19836 startIndex = startIndex || 0;
19837 endIndex = endIndex || ns.length - 1;
19838 for(var i = startIndex; i <= endIndex; i++){
19839 ns[i].nodeIndex = i;
19844 * Changes the data store this view uses and refresh the view.
19845 * @param {Store} store
19847 setStore : function(store, initial){
19848 if(!initial && this.store){
19849 this.store.un("datachanged", this.refresh);
19850 this.store.un("add", this.onAdd);
19851 this.store.un("remove", this.onRemove);
19852 this.store.un("update", this.onUpdate);
19853 this.store.un("clear", this.refresh);
19854 this.store.un("beforeload", this.onBeforeLoad);
19855 this.store.un("load", this.onLoad);
19856 this.store.un("loadexception", this.onLoad);
19860 store.on("datachanged", this.refresh, this);
19861 store.on("add", this.onAdd, this);
19862 store.on("remove", this.onRemove, this);
19863 store.on("update", this.onUpdate, this);
19864 store.on("clear", this.refresh, this);
19865 store.on("beforeload", this.onBeforeLoad, this);
19866 store.on("load", this.onLoad, this);
19867 store.on("loadexception", this.onLoad, this);
19875 * onbeforeLoad - masks the loading area.
19878 onBeforeLoad : function(store,opts)
19880 //Roo.log('onBeforeLoad');
19882 this.el.update("");
19884 this.el.mask(this.mask ? this.mask : "Loading" );
19886 onLoad : function ()
19893 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19894 * @param {HTMLElement} node
19895 * @return {HTMLElement} The template node
19897 findItemFromChild : function(node){
19898 var el = this.dataName ?
19899 this.el.child('.roo-tpl-' + this.dataName,true) :
19902 if(!node || node.parentNode == el){
19905 var p = node.parentNode;
19906 while(p && p != el){
19907 if(p.parentNode == el){
19916 onClick : function(e){
19917 var item = this.findItemFromChild(e.getTarget());
19919 var index = this.indexOf(item);
19920 if(this.onItemClick(item, index, e) !== false){
19921 this.fireEvent("click", this, index, item, e);
19924 this.clearSelections();
19929 onContextMenu : function(e){
19930 var item = this.findItemFromChild(e.getTarget());
19932 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19937 onDblClick : function(e){
19938 var item = this.findItemFromChild(e.getTarget());
19940 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19944 onItemClick : function(item, index, e)
19946 if(this.fireEvent("beforeclick", this, index, item, e) === false){
19949 if (this.toggleSelect) {
19950 var m = this.isSelected(item) ? 'unselect' : 'select';
19953 _t[m](item, true, false);
19956 if(this.multiSelect || this.singleSelect){
19957 if(this.multiSelect && e.shiftKey && this.lastSelection){
19958 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19960 this.select(item, this.multiSelect && e.ctrlKey);
19961 this.lastSelection = item;
19964 if(!this.tickable){
19965 e.preventDefault();
19973 * Get the number of selected nodes.
19976 getSelectionCount : function(){
19977 return this.selections.length;
19981 * Get the currently selected nodes.
19982 * @return {Array} An array of HTMLElements
19984 getSelectedNodes : function(){
19985 return this.selections;
19989 * Get the indexes of the selected nodes.
19992 getSelectedIndexes : function(){
19993 var indexes = [], s = this.selections;
19994 for(var i = 0, len = s.length; i < len; i++){
19995 indexes.push(s[i].nodeIndex);
20001 * Clear all selections
20002 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20004 clearSelections : function(suppressEvent){
20005 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20006 this.cmp.elements = this.selections;
20007 this.cmp.removeClass(this.selectedClass);
20008 this.selections = [];
20009 if(!suppressEvent){
20010 this.fireEvent("selectionchange", this, this.selections);
20016 * Returns true if the passed node is selected
20017 * @param {HTMLElement/Number} node The node or node index
20018 * @return {Boolean}
20020 isSelected : function(node){
20021 var s = this.selections;
20025 node = this.getNode(node);
20026 return s.indexOf(node) !== -1;
20031 * @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
20032 * @param {Boolean} keepExisting (optional) true to keep existing selections
20033 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20035 select : function(nodeInfo, keepExisting, suppressEvent){
20036 if(nodeInfo instanceof Array){
20038 this.clearSelections(true);
20040 for(var i = 0, len = nodeInfo.length; i < len; i++){
20041 this.select(nodeInfo[i], true, true);
20045 var node = this.getNode(nodeInfo);
20046 if(!node || this.isSelected(node)){
20047 return; // already selected.
20050 this.clearSelections(true);
20053 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20054 Roo.fly(node).addClass(this.selectedClass);
20055 this.selections.push(node);
20056 if(!suppressEvent){
20057 this.fireEvent("selectionchange", this, this.selections);
20065 * @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
20066 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20067 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20069 unselect : function(nodeInfo, keepExisting, suppressEvent)
20071 if(nodeInfo instanceof Array){
20072 Roo.each(this.selections, function(s) {
20073 this.unselect(s, nodeInfo);
20077 var node = this.getNode(nodeInfo);
20078 if(!node || !this.isSelected(node)){
20079 //Roo.log("not selected");
20080 return; // not selected.
20084 Roo.each(this.selections, function(s) {
20086 Roo.fly(node).removeClass(this.selectedClass);
20093 this.selections= ns;
20094 this.fireEvent("selectionchange", this, this.selections);
20098 * Gets a template node.
20099 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20100 * @return {HTMLElement} The node or null if it wasn't found
20102 getNode : function(nodeInfo){
20103 if(typeof nodeInfo == "string"){
20104 return document.getElementById(nodeInfo);
20105 }else if(typeof nodeInfo == "number"){
20106 return this.nodes[nodeInfo];
20112 * Gets a range template nodes.
20113 * @param {Number} startIndex
20114 * @param {Number} endIndex
20115 * @return {Array} An array of nodes
20117 getNodes : function(start, end){
20118 var ns = this.nodes;
20119 start = start || 0;
20120 end = typeof end == "undefined" ? ns.length - 1 : end;
20123 for(var i = start; i <= end; i++){
20127 for(var i = start; i >= end; i--){
20135 * Finds the index of the passed node
20136 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20137 * @return {Number} The index of the node or -1
20139 indexOf : function(node){
20140 node = this.getNode(node);
20141 if(typeof node.nodeIndex == "number"){
20142 return node.nodeIndex;
20144 var ns = this.nodes;
20145 for(var i = 0, len = ns.length; i < len; i++){
20156 * based on jquery fullcalendar
20160 Roo.bootstrap = Roo.bootstrap || {};
20162 * @class Roo.bootstrap.Calendar
20163 * @extends Roo.bootstrap.Component
20164 * Bootstrap Calendar class
20165 * @cfg {Boolean} loadMask (true|false) default false
20166 * @cfg {Object} header generate the user specific header of the calendar, default false
20169 * Create a new Container
20170 * @param {Object} config The config object
20175 Roo.bootstrap.Calendar = function(config){
20176 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20180 * Fires when a date is selected
20181 * @param {DatePicker} this
20182 * @param {Date} date The selected date
20186 * @event monthchange
20187 * Fires when the displayed month changes
20188 * @param {DatePicker} this
20189 * @param {Date} date The selected month
20191 'monthchange': true,
20193 * @event evententer
20194 * Fires when mouse over an event
20195 * @param {Calendar} this
20196 * @param {event} Event
20198 'evententer': true,
20200 * @event eventleave
20201 * Fires when the mouse leaves an
20202 * @param {Calendar} this
20205 'eventleave': true,
20207 * @event eventclick
20208 * Fires when the mouse click an
20209 * @param {Calendar} this
20218 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20221 * @cfg {Roo.data.Store} store
20222 * The data source for the calendar
20226 * @cfg {Number} startDay
20227 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20235 getAutoCreate : function(){
20238 var fc_button = function(name, corner, style, content ) {
20239 return Roo.apply({},{
20241 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20243 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20246 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20257 style : 'width:100%',
20264 cls : 'fc-header-left',
20266 fc_button('prev', 'left', 'arrow', '‹' ),
20267 fc_button('next', 'right', 'arrow', '›' ),
20268 { tag: 'span', cls: 'fc-header-space' },
20269 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20277 cls : 'fc-header-center',
20281 cls: 'fc-header-title',
20284 html : 'month / year'
20292 cls : 'fc-header-right',
20294 /* fc_button('month', 'left', '', 'month' ),
20295 fc_button('week', '', '', 'week' ),
20296 fc_button('day', 'right', '', 'day' )
20308 header = this.header;
20311 var cal_heads = function() {
20313 // fixme - handle this.
20315 for (var i =0; i < Date.dayNames.length; i++) {
20316 var d = Date.dayNames[i];
20319 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20320 html : d.substring(0,3)
20324 ret[0].cls += ' fc-first';
20325 ret[6].cls += ' fc-last';
20328 var cal_cell = function(n) {
20331 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20336 cls: 'fc-day-number',
20340 cls: 'fc-day-content',
20344 style: 'position: relative;' // height: 17px;
20356 var cal_rows = function() {
20359 for (var r = 0; r < 6; r++) {
20366 for (var i =0; i < Date.dayNames.length; i++) {
20367 var d = Date.dayNames[i];
20368 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20371 row.cn[0].cls+=' fc-first';
20372 row.cn[0].cn[0].style = 'min-height:90px';
20373 row.cn[6].cls+=' fc-last';
20377 ret[0].cls += ' fc-first';
20378 ret[4].cls += ' fc-prev-last';
20379 ret[5].cls += ' fc-last';
20386 cls: 'fc-border-separate',
20387 style : 'width:100%',
20395 cls : 'fc-first fc-last',
20413 cls : 'fc-content',
20414 style : "position: relative;",
20417 cls : 'fc-view fc-view-month fc-grid',
20418 style : 'position: relative',
20419 unselectable : 'on',
20422 cls : 'fc-event-container',
20423 style : 'position:absolute;z-index:8;top:0;left:0;'
20441 initEvents : function()
20444 throw "can not find store for calendar";
20450 style: "text-align:center",
20454 style: "background-color:white;width:50%;margin:250 auto",
20458 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20469 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20471 var size = this.el.select('.fc-content', true).first().getSize();
20472 this.maskEl.setSize(size.width, size.height);
20473 this.maskEl.enableDisplayMode("block");
20474 if(!this.loadMask){
20475 this.maskEl.hide();
20478 this.store = Roo.factory(this.store, Roo.data);
20479 this.store.on('load', this.onLoad, this);
20480 this.store.on('beforeload', this.onBeforeLoad, this);
20484 this.cells = this.el.select('.fc-day',true);
20485 //Roo.log(this.cells);
20486 this.textNodes = this.el.query('.fc-day-number');
20487 this.cells.addClassOnOver('fc-state-hover');
20489 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20490 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20491 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20492 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20494 this.on('monthchange', this.onMonthChange, this);
20496 this.update(new Date().clearTime());
20499 resize : function() {
20500 var sz = this.el.getSize();
20502 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20503 this.el.select('.fc-day-content div',true).setHeight(34);
20508 showPrevMonth : function(e){
20509 this.update(this.activeDate.add("mo", -1));
20511 showToday : function(e){
20512 this.update(new Date().clearTime());
20515 showNextMonth : function(e){
20516 this.update(this.activeDate.add("mo", 1));
20520 showPrevYear : function(){
20521 this.update(this.activeDate.add("y", -1));
20525 showNextYear : function(){
20526 this.update(this.activeDate.add("y", 1));
20531 update : function(date)
20533 var vd = this.activeDate;
20534 this.activeDate = date;
20535 // if(vd && this.el){
20536 // var t = date.getTime();
20537 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20538 // Roo.log('using add remove');
20540 // this.fireEvent('monthchange', this, date);
20542 // this.cells.removeClass("fc-state-highlight");
20543 // this.cells.each(function(c){
20544 // if(c.dateValue == t){
20545 // c.addClass("fc-state-highlight");
20546 // setTimeout(function(){
20547 // try{c.dom.firstChild.focus();}catch(e){}
20557 var days = date.getDaysInMonth();
20559 var firstOfMonth = date.getFirstDateOfMonth();
20560 var startingPos = firstOfMonth.getDay()-this.startDay;
20562 if(startingPos < this.startDay){
20566 var pm = date.add(Date.MONTH, -1);
20567 var prevStart = pm.getDaysInMonth()-startingPos;
20569 this.cells = this.el.select('.fc-day',true);
20570 this.textNodes = this.el.query('.fc-day-number');
20571 this.cells.addClassOnOver('fc-state-hover');
20573 var cells = this.cells.elements;
20574 var textEls = this.textNodes;
20576 Roo.each(cells, function(cell){
20577 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20580 days += startingPos;
20582 // convert everything to numbers so it's fast
20583 var day = 86400000;
20584 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20587 //Roo.log(prevStart);
20589 var today = new Date().clearTime().getTime();
20590 var sel = date.clearTime().getTime();
20591 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20592 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20593 var ddMatch = this.disabledDatesRE;
20594 var ddText = this.disabledDatesText;
20595 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20596 var ddaysText = this.disabledDaysText;
20597 var format = this.format;
20599 var setCellClass = function(cal, cell){
20603 //Roo.log('set Cell Class');
20605 var t = d.getTime();
20609 cell.dateValue = t;
20611 cell.className += " fc-today";
20612 cell.className += " fc-state-highlight";
20613 cell.title = cal.todayText;
20616 // disable highlight in other month..
20617 //cell.className += " fc-state-highlight";
20622 cell.className = " fc-state-disabled";
20623 cell.title = cal.minText;
20627 cell.className = " fc-state-disabled";
20628 cell.title = cal.maxText;
20632 if(ddays.indexOf(d.getDay()) != -1){
20633 cell.title = ddaysText;
20634 cell.className = " fc-state-disabled";
20637 if(ddMatch && format){
20638 var fvalue = d.dateFormat(format);
20639 if(ddMatch.test(fvalue)){
20640 cell.title = ddText.replace("%0", fvalue);
20641 cell.className = " fc-state-disabled";
20645 if (!cell.initialClassName) {
20646 cell.initialClassName = cell.dom.className;
20649 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20654 for(; i < startingPos; i++) {
20655 textEls[i].innerHTML = (++prevStart);
20656 d.setDate(d.getDate()+1);
20658 cells[i].className = "fc-past fc-other-month";
20659 setCellClass(this, cells[i]);
20664 for(; i < days; i++){
20665 intDay = i - startingPos + 1;
20666 textEls[i].innerHTML = (intDay);
20667 d.setDate(d.getDate()+1);
20669 cells[i].className = ''; // "x-date-active";
20670 setCellClass(this, cells[i]);
20674 for(; i < 42; i++) {
20675 textEls[i].innerHTML = (++extraDays);
20676 d.setDate(d.getDate()+1);
20678 cells[i].className = "fc-future fc-other-month";
20679 setCellClass(this, cells[i]);
20682 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20684 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20686 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20687 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20689 if(totalRows != 6){
20690 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20691 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20694 this.fireEvent('monthchange', this, date);
20698 if(!this.internalRender){
20699 var main = this.el.dom.firstChild;
20700 var w = main.offsetWidth;
20701 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20702 Roo.fly(main).setWidth(w);
20703 this.internalRender = true;
20704 // opera does not respect the auto grow header center column
20705 // then, after it gets a width opera refuses to recalculate
20706 // without a second pass
20707 if(Roo.isOpera && !this.secondPass){
20708 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20709 this.secondPass = true;
20710 this.update.defer(10, this, [date]);
20717 findCell : function(dt) {
20718 dt = dt.clearTime().getTime();
20720 this.cells.each(function(c){
20721 //Roo.log("check " +c.dateValue + '?=' + dt);
20722 if(c.dateValue == dt){
20732 findCells : function(ev) {
20733 var s = ev.start.clone().clearTime().getTime();
20735 var e= ev.end.clone().clearTime().getTime();
20738 this.cells.each(function(c){
20739 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20741 if(c.dateValue > e){
20744 if(c.dateValue < s){
20753 // findBestRow: function(cells)
20757 // for (var i =0 ; i < cells.length;i++) {
20758 // ret = Math.max(cells[i].rows || 0,ret);
20765 addItem : function(ev)
20767 // look for vertical location slot in
20768 var cells = this.findCells(ev);
20770 // ev.row = this.findBestRow(cells);
20772 // work out the location.
20776 for(var i =0; i < cells.length; i++) {
20778 cells[i].row = cells[0].row;
20781 cells[i].row = cells[i].row + 1;
20791 if (crow.start.getY() == cells[i].getY()) {
20793 crow.end = cells[i];
20810 cells[0].events.push(ev);
20812 this.calevents.push(ev);
20815 clearEvents: function() {
20817 if(!this.calevents){
20821 Roo.each(this.cells.elements, function(c){
20827 Roo.each(this.calevents, function(e) {
20828 Roo.each(e.els, function(el) {
20829 el.un('mouseenter' ,this.onEventEnter, this);
20830 el.un('mouseleave' ,this.onEventLeave, this);
20835 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20841 renderEvents: function()
20845 this.cells.each(function(c) {
20854 if(c.row != c.events.length){
20855 r = 4 - (4 - (c.row - c.events.length));
20858 c.events = ev.slice(0, r);
20859 c.more = ev.slice(r);
20861 if(c.more.length && c.more.length == 1){
20862 c.events.push(c.more.pop());
20865 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20869 this.cells.each(function(c) {
20871 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20874 for (var e = 0; e < c.events.length; e++){
20875 var ev = c.events[e];
20876 var rows = ev.rows;
20878 for(var i = 0; i < rows.length; i++) {
20880 // how many rows should it span..
20883 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20884 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20886 unselectable : "on",
20889 cls: 'fc-event-inner',
20893 // cls: 'fc-event-time',
20894 // html : cells.length > 1 ? '' : ev.time
20898 cls: 'fc-event-title',
20899 html : String.format('{0}', ev.title)
20906 cls: 'ui-resizable-handle ui-resizable-e',
20907 html : '  '
20914 cfg.cls += ' fc-event-start';
20916 if ((i+1) == rows.length) {
20917 cfg.cls += ' fc-event-end';
20920 var ctr = _this.el.select('.fc-event-container',true).first();
20921 var cg = ctr.createChild(cfg);
20923 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20924 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20926 var r = (c.more.length) ? 1 : 0;
20927 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20928 cg.setWidth(ebox.right - sbox.x -2);
20930 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20931 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20932 cg.on('click', _this.onEventClick, _this, ev);
20943 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20944 style : 'position: absolute',
20945 unselectable : "on",
20948 cls: 'fc-event-inner',
20952 cls: 'fc-event-title',
20960 cls: 'ui-resizable-handle ui-resizable-e',
20961 html : '  '
20967 var ctr = _this.el.select('.fc-event-container',true).first();
20968 var cg = ctr.createChild(cfg);
20970 var sbox = c.select('.fc-day-content',true).first().getBox();
20971 var ebox = c.select('.fc-day-content',true).first().getBox();
20973 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
20974 cg.setWidth(ebox.right - sbox.x -2);
20976 cg.on('click', _this.onMoreEventClick, _this, c.more);
20986 onEventEnter: function (e, el,event,d) {
20987 this.fireEvent('evententer', this, el, event);
20990 onEventLeave: function (e, el,event,d) {
20991 this.fireEvent('eventleave', this, el, event);
20994 onEventClick: function (e, el,event,d) {
20995 this.fireEvent('eventclick', this, el, event);
20998 onMonthChange: function () {
21002 onMoreEventClick: function(e, el, more)
21006 this.calpopover.placement = 'right';
21007 this.calpopover.setTitle('More');
21009 this.calpopover.setContent('');
21011 var ctr = this.calpopover.el.select('.popover-content', true).first();
21013 Roo.each(more, function(m){
21015 cls : 'fc-event-hori fc-event-draggable',
21018 var cg = ctr.createChild(cfg);
21020 cg.on('click', _this.onEventClick, _this, m);
21023 this.calpopover.show(el);
21028 onLoad: function ()
21030 this.calevents = [];
21033 if(this.store.getCount() > 0){
21034 this.store.data.each(function(d){
21037 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21038 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21039 time : d.data.start_time,
21040 title : d.data.title,
21041 description : d.data.description,
21042 venue : d.data.venue
21047 this.renderEvents();
21049 if(this.calevents.length && this.loadMask){
21050 this.maskEl.hide();
21054 onBeforeLoad: function()
21056 this.clearEvents();
21058 this.maskEl.show();
21072 * @class Roo.bootstrap.Popover
21073 * @extends Roo.bootstrap.Component
21075 * Bootstrap Popover class
21076 * @cfg {String} html contents of the popover (or false to use children..)
21077 * @cfg {String} title of popover (or false to hide)
21078 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21079 * @cfg {String} trigger click || hover (or false to trigger manually)
21080 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21081 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21082 * - if false and it has a 'parent' then it will be automatically added to that element
21083 * - if string - Roo.get will be called
21084 * @cfg {Number} delay - delay before showing
21087 * Create a new Popover
21088 * @param {Object} config The config object
21091 Roo.bootstrap.Popover = function(config){
21092 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21098 * After the popover show
21100 * @param {Roo.bootstrap.Popover} this
21105 * After the popover hide
21107 * @param {Roo.bootstrap.Popover} this
21113 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21118 placement : 'right',
21119 trigger : 'hover', // hover
21125 can_build_overlaid : false,
21127 maskEl : false, // the mask element
21130 alignEl : false, // when show is called with an element - this get's stored.
21132 getChildContainer : function()
21134 return this.contentEl;
21137 getPopoverHeader : function()
21139 this.title = true; // flag not to hide it..
21140 this.headerEl.addClass('p-0');
21141 return this.headerEl
21145 getAutoCreate : function(){
21148 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21149 style: 'display:block',
21155 cls : 'popover-inner ',
21159 cls: 'popover-title popover-header',
21160 html : this.title === false ? '' : this.title
21163 cls : 'popover-content popover-body ' + (this.cls || ''),
21164 html : this.html || ''
21175 * @param {string} the title
21177 setTitle: function(str)
21181 this.headerEl.dom.innerHTML = str;
21186 * @param {string} the body content
21188 setContent: function(str)
21191 if (this.contentEl) {
21192 this.contentEl.dom.innerHTML = str;
21196 // as it get's added to the bottom of the page.
21197 onRender : function(ct, position)
21199 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21204 var cfg = Roo.apply({}, this.getAutoCreate());
21208 cfg.cls += ' ' + this.cls;
21211 cfg.style = this.style;
21213 //Roo.log("adding to ");
21214 this.el = Roo.get(document.body).createChild(cfg, position);
21215 // Roo.log(this.el);
21218 this.contentEl = this.el.select('.popover-content',true).first();
21219 this.headerEl = this.el.select('.popover-title',true).first();
21222 if(typeof(this.items) != 'undefined'){
21223 var items = this.items;
21226 for(var i =0;i < items.length;i++) {
21227 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21231 this.items = nitems;
21233 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21234 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21241 resizeMask : function()
21243 this.maskEl.setSize(
21244 Roo.lib.Dom.getViewWidth(true),
21245 Roo.lib.Dom.getViewHeight(true)
21249 initEvents : function()
21253 Roo.bootstrap.Popover.register(this);
21256 this.arrowEl = this.el.select('.arrow',true).first();
21257 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21258 this.el.enableDisplayMode('block');
21262 if (this.over === false && !this.parent()) {
21265 if (this.triggers === false) {
21270 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21271 var triggers = this.trigger ? this.trigger.split(' ') : [];
21272 Roo.each(triggers, function(trigger) {
21274 if (trigger == 'click') {
21275 on_el.on('click', this.toggle, this);
21276 } else if (trigger != 'manual') {
21277 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21278 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21280 on_el.on(eventIn ,this.enter, this);
21281 on_el.on(eventOut, this.leave, this);
21291 toggle : function () {
21292 this.hoverState == 'in' ? this.leave() : this.enter();
21295 enter : function () {
21297 clearTimeout(this.timeout);
21299 this.hoverState = 'in';
21301 if (!this.delay || !this.delay.show) {
21306 this.timeout = setTimeout(function () {
21307 if (_t.hoverState == 'in') {
21310 }, this.delay.show)
21313 leave : function() {
21314 clearTimeout(this.timeout);
21316 this.hoverState = 'out';
21318 if (!this.delay || !this.delay.hide) {
21323 this.timeout = setTimeout(function () {
21324 if (_t.hoverState == 'out') {
21327 }, this.delay.hide)
21331 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21332 * @param {string} (left|right|top|bottom) position
21334 show : function (on_el, placement)
21336 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21337 on_el = on_el || false; // default to false
21340 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21341 on_el = this.parent().el;
21342 } else if (this.over) {
21343 on_el = Roo.get(this.over);
21348 this.alignEl = Roo.get( on_el );
21351 this.render(document.body);
21357 if (this.title === false) {
21358 this.headerEl.hide();
21363 this.el.dom.style.display = 'block';
21366 if (this.alignEl) {
21367 this.updatePosition(this.placement, true);
21370 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21371 var es = this.el.getSize();
21372 var x = Roo.lib.Dom.getViewWidth()/2;
21373 var y = Roo.lib.Dom.getViewHeight()/2;
21374 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21379 //var arrow = this.el.select('.arrow',true).first();
21380 //arrow.set(align[2],
21382 this.el.addClass('in');
21386 this.hoverState = 'in';
21389 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21390 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21391 this.maskEl.dom.style.display = 'block';
21392 this.maskEl.addClass('show');
21394 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21396 this.fireEvent('show', this);
21400 * fire this manually after loading a grid in the table for example
21401 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21402 * @param {Boolean} try and move it if we cant get right position.
21404 updatePosition : function(placement, try_move)
21406 // allow for calling with no parameters
21407 placement = placement ? placement : this.placement;
21408 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21410 this.el.removeClass([
21411 'fade','top','bottom', 'left', 'right','in',
21412 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21414 this.el.addClass(placement + ' bs-popover-' + placement);
21416 if (!this.alignEl ) {
21420 switch (placement) {
21422 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21423 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21424 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21425 //normal display... or moved up/down.
21426 this.el.setXY(offset);
21427 var xy = this.alignEl.getAnchorXY('tr', false);
21429 this.arrowEl.setXY(xy);
21432 // continue through...
21433 return this.updatePosition('left', false);
21437 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21438 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21439 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21440 //normal display... or moved up/down.
21441 this.el.setXY(offset);
21442 var xy = this.alignEl.getAnchorXY('tl', false);
21443 xy[0]-=10;xy[1]+=5; // << fix me
21444 this.arrowEl.setXY(xy);
21448 return this.updatePosition('right', false);
21451 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21452 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21453 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21454 //normal display... or moved up/down.
21455 this.el.setXY(offset);
21456 var xy = this.alignEl.getAnchorXY('t', false);
21457 xy[1]-=10; // << fix me
21458 this.arrowEl.setXY(xy);
21462 return this.updatePosition('bottom', false);
21465 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21466 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21467 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21468 //normal display... or moved up/down.
21469 this.el.setXY(offset);
21470 var xy = this.alignEl.getAnchorXY('b', false);
21471 xy[1]+=2; // << fix me
21472 this.arrowEl.setXY(xy);
21476 return this.updatePosition('top', false);
21487 this.el.setXY([0,0]);
21488 this.el.removeClass('in');
21490 this.hoverState = null;
21491 this.maskEl.hide(); // always..
21492 this.fireEvent('hide', this);
21498 Roo.apply(Roo.bootstrap.Popover, {
21501 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21502 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21503 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21504 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21509 clickHander : false,
21513 onMouseDown : function(e)
21515 if (this.popups.length && !e.getTarget(".roo-popover")) {
21516 /// what is nothing is showing..
21525 register : function(popup)
21527 if (!Roo.bootstrap.Popover.clickHandler) {
21528 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21530 // hide other popups.
21531 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21532 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21533 this.hideAll(); //<< why?
21534 //this.popups.push(popup);
21536 hideAll : function()
21538 this.popups.forEach(function(p) {
21542 onShow : function() {
21543 Roo.bootstrap.Popover.popups.push(this);
21545 onHide : function() {
21546 Roo.bootstrap.Popover.popups.remove(this);
21552 * Card header - holder for the card header elements.
21557 * @class Roo.bootstrap.PopoverNav
21558 * @extends Roo.bootstrap.NavGroup
21559 * Bootstrap Popover header navigation class
21561 * Create a new Popover Header Navigation
21562 * @param {Object} config The config object
21565 Roo.bootstrap.PopoverNav = function(config){
21566 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21569 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21572 container_method : 'getPopoverHeader'
21590 * @class Roo.bootstrap.Progress
21591 * @extends Roo.bootstrap.Component
21592 * Bootstrap Progress class
21593 * @cfg {Boolean} striped striped of the progress bar
21594 * @cfg {Boolean} active animated of the progress bar
21598 * Create a new Progress
21599 * @param {Object} config The config object
21602 Roo.bootstrap.Progress = function(config){
21603 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21606 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21611 getAutoCreate : function(){
21619 cfg.cls += ' progress-striped';
21623 cfg.cls += ' active';
21642 * @class Roo.bootstrap.ProgressBar
21643 * @extends Roo.bootstrap.Component
21644 * Bootstrap ProgressBar class
21645 * @cfg {Number} aria_valuenow aria-value now
21646 * @cfg {Number} aria_valuemin aria-value min
21647 * @cfg {Number} aria_valuemax aria-value max
21648 * @cfg {String} label label for the progress bar
21649 * @cfg {String} panel (success | info | warning | danger )
21650 * @cfg {String} role role of the progress bar
21651 * @cfg {String} sr_only text
21655 * Create a new ProgressBar
21656 * @param {Object} config The config object
21659 Roo.bootstrap.ProgressBar = function(config){
21660 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21663 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21667 aria_valuemax : 100,
21673 getAutoCreate : function()
21678 cls: 'progress-bar',
21679 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21691 cfg.role = this.role;
21694 if(this.aria_valuenow){
21695 cfg['aria-valuenow'] = this.aria_valuenow;
21698 if(this.aria_valuemin){
21699 cfg['aria-valuemin'] = this.aria_valuemin;
21702 if(this.aria_valuemax){
21703 cfg['aria-valuemax'] = this.aria_valuemax;
21706 if(this.label && !this.sr_only){
21707 cfg.html = this.label;
21711 cfg.cls += ' progress-bar-' + this.panel;
21717 update : function(aria_valuenow)
21719 this.aria_valuenow = aria_valuenow;
21721 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21736 * @class Roo.bootstrap.TabGroup
21737 * @extends Roo.bootstrap.Column
21738 * Bootstrap Column class
21739 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21740 * @cfg {Boolean} carousel true to make the group behave like a carousel
21741 * @cfg {Boolean} bullets show bullets for the panels
21742 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21743 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21744 * @cfg {Boolean} showarrow (true|false) show arrow default true
21747 * Create a new TabGroup
21748 * @param {Object} config The config object
21751 Roo.bootstrap.TabGroup = function(config){
21752 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21754 this.navId = Roo.id();
21757 Roo.bootstrap.TabGroup.register(this);
21761 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21764 transition : false,
21769 slideOnTouch : false,
21772 getAutoCreate : function()
21774 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21776 cfg.cls += ' tab-content';
21778 if (this.carousel) {
21779 cfg.cls += ' carousel slide';
21782 cls : 'carousel-inner',
21786 if(this.bullets && !Roo.isTouch){
21789 cls : 'carousel-bullets',
21793 if(this.bullets_cls){
21794 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21801 cfg.cn[0].cn.push(bullets);
21804 if(this.showarrow){
21805 cfg.cn[0].cn.push({
21807 class : 'carousel-arrow',
21811 class : 'carousel-prev',
21815 class : 'fa fa-chevron-left'
21821 class : 'carousel-next',
21825 class : 'fa fa-chevron-right'
21838 initEvents: function()
21840 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21841 // this.el.on("touchstart", this.onTouchStart, this);
21844 if(this.autoslide){
21847 this.slideFn = window.setInterval(function() {
21848 _this.showPanelNext();
21852 if(this.showarrow){
21853 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21854 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21860 // onTouchStart : function(e, el, o)
21862 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21866 // this.showPanelNext();
21870 getChildContainer : function()
21872 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21876 * register a Navigation item
21877 * @param {Roo.bootstrap.NavItem} the navitem to add
21879 register : function(item)
21881 this.tabs.push( item);
21882 item.navId = this.navId; // not really needed..
21887 getActivePanel : function()
21890 Roo.each(this.tabs, function(t) {
21900 getPanelByName : function(n)
21903 Roo.each(this.tabs, function(t) {
21904 if (t.tabId == n) {
21912 indexOfPanel : function(p)
21915 Roo.each(this.tabs, function(t,i) {
21916 if (t.tabId == p.tabId) {
21925 * show a specific panel
21926 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21927 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21929 showPanel : function (pan)
21931 if(this.transition || typeof(pan) == 'undefined'){
21932 Roo.log("waiting for the transitionend");
21936 if (typeof(pan) == 'number') {
21937 pan = this.tabs[pan];
21940 if (typeof(pan) == 'string') {
21941 pan = this.getPanelByName(pan);
21944 var cur = this.getActivePanel();
21947 Roo.log('pan or acitve pan is undefined');
21951 if (pan.tabId == this.getActivePanel().tabId) {
21955 if (false === cur.fireEvent('beforedeactivate')) {
21959 if(this.bullets > 0 && !Roo.isTouch){
21960 this.setActiveBullet(this.indexOfPanel(pan));
21963 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21965 //class="carousel-item carousel-item-next carousel-item-left"
21967 this.transition = true;
21968 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
21969 var lr = dir == 'next' ? 'left' : 'right';
21970 pan.el.addClass(dir); // or prev
21971 pan.el.addClass('carousel-item-' + dir); // or prev
21972 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21973 cur.el.addClass(lr); // or right
21974 pan.el.addClass(lr);
21975 cur.el.addClass('carousel-item-' +lr); // or right
21976 pan.el.addClass('carousel-item-' +lr);
21980 cur.el.on('transitionend', function() {
21981 Roo.log("trans end?");
21983 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21984 pan.setActive(true);
21986 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21987 cur.setActive(false);
21989 _this.transition = false;
21991 }, this, { single: true } );
21996 cur.setActive(false);
21997 pan.setActive(true);
22002 showPanelNext : function()
22004 var i = this.indexOfPanel(this.getActivePanel());
22006 if (i >= this.tabs.length - 1 && !this.autoslide) {
22010 if (i >= this.tabs.length - 1 && this.autoslide) {
22014 this.showPanel(this.tabs[i+1]);
22017 showPanelPrev : function()
22019 var i = this.indexOfPanel(this.getActivePanel());
22021 if (i < 1 && !this.autoslide) {
22025 if (i < 1 && this.autoslide) {
22026 i = this.tabs.length;
22029 this.showPanel(this.tabs[i-1]);
22033 addBullet: function()
22035 if(!this.bullets || Roo.isTouch){
22038 var ctr = this.el.select('.carousel-bullets',true).first();
22039 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22040 var bullet = ctr.createChild({
22041 cls : 'bullet bullet-' + i
22042 },ctr.dom.lastChild);
22047 bullet.on('click', (function(e, el, o, ii, t){
22049 e.preventDefault();
22051 this.showPanel(ii);
22053 if(this.autoslide && this.slideFn){
22054 clearInterval(this.slideFn);
22055 this.slideFn = window.setInterval(function() {
22056 _this.showPanelNext();
22060 }).createDelegate(this, [i, bullet], true));
22065 setActiveBullet : function(i)
22071 Roo.each(this.el.select('.bullet', true).elements, function(el){
22072 el.removeClass('selected');
22075 var bullet = this.el.select('.bullet-' + i, true).first();
22081 bullet.addClass('selected');
22092 Roo.apply(Roo.bootstrap.TabGroup, {
22096 * register a Navigation Group
22097 * @param {Roo.bootstrap.NavGroup} the navgroup to add
22099 register : function(navgrp)
22101 this.groups[navgrp.navId] = navgrp;
22105 * fetch a Navigation Group based on the navigation ID
22106 * if one does not exist , it will get created.
22107 * @param {string} the navgroup to add
22108 * @returns {Roo.bootstrap.NavGroup} the navgroup
22110 get: function(navId) {
22111 if (typeof(this.groups[navId]) == 'undefined') {
22112 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22114 return this.groups[navId] ;
22129 * @class Roo.bootstrap.TabPanel
22130 * @extends Roo.bootstrap.Component
22131 * @children Roo.bootstrap.Component
22132 * Bootstrap TabPanel class
22133 * @cfg {Boolean} active panel active
22134 * @cfg {String} html panel content
22135 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22136 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22137 * @cfg {String} href click to link..
22138 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22142 * Create a new TabPanel
22143 * @param {Object} config The config object
22146 Roo.bootstrap.TabPanel = function(config){
22147 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22151 * Fires when the active status changes
22152 * @param {Roo.bootstrap.TabPanel} this
22153 * @param {Boolean} state the new state
22158 * @event beforedeactivate
22159 * Fires before a tab is de-activated - can be used to do validation on a form.
22160 * @param {Roo.bootstrap.TabPanel} this
22161 * @return {Boolean} false if there is an error
22164 'beforedeactivate': true
22167 this.tabId = this.tabId || Roo.id();
22171 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22178 touchSlide : false,
22179 getAutoCreate : function(){
22184 // item is needed for carousel - not sure if it has any effect otherwise
22185 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22186 html: this.html || ''
22190 cfg.cls += ' active';
22194 cfg.tabId = this.tabId;
22202 initEvents: function()
22204 var p = this.parent();
22206 this.navId = this.navId || p.navId;
22208 if (typeof(this.navId) != 'undefined') {
22209 // not really needed.. but just in case.. parent should be a NavGroup.
22210 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22214 var i = tg.tabs.length - 1;
22216 if(this.active && tg.bullets > 0 && i < tg.bullets){
22217 tg.setActiveBullet(i);
22221 this.el.on('click', this.onClick, this);
22223 if(Roo.isTouch && this.touchSlide){
22224 this.el.on("touchstart", this.onTouchStart, this);
22225 this.el.on("touchmove", this.onTouchMove, this);
22226 this.el.on("touchend", this.onTouchEnd, this);
22231 onRender : function(ct, position)
22233 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22236 setActive : function(state)
22238 Roo.log("panel - set active " + this.tabId + "=" + state);
22240 this.active = state;
22242 this.el.removeClass('active');
22244 } else if (!this.el.hasClass('active')) {
22245 this.el.addClass('active');
22248 this.fireEvent('changed', this, state);
22251 onClick : function(e)
22253 e.preventDefault();
22255 if(!this.href.length){
22259 window.location.href = this.href;
22268 onTouchStart : function(e)
22270 this.swiping = false;
22272 this.startX = e.browserEvent.touches[0].clientX;
22273 this.startY = e.browserEvent.touches[0].clientY;
22276 onTouchMove : function(e)
22278 this.swiping = true;
22280 this.endX = e.browserEvent.touches[0].clientX;
22281 this.endY = e.browserEvent.touches[0].clientY;
22284 onTouchEnd : function(e)
22291 var tabGroup = this.parent();
22293 if(this.endX > this.startX){ // swiping right
22294 tabGroup.showPanelPrev();
22298 if(this.startX > this.endX){ // swiping left
22299 tabGroup.showPanelNext();
22318 * @class Roo.bootstrap.DateField
22319 * @extends Roo.bootstrap.Input
22320 * Bootstrap DateField class
22321 * @cfg {Number} weekStart default 0
22322 * @cfg {String} viewMode default empty, (months|years)
22323 * @cfg {String} minViewMode default empty, (months|years)
22324 * @cfg {Number} startDate default -Infinity
22325 * @cfg {Number} endDate default Infinity
22326 * @cfg {Boolean} todayHighlight default false
22327 * @cfg {Boolean} todayBtn default false
22328 * @cfg {Boolean} calendarWeeks default false
22329 * @cfg {Object} daysOfWeekDisabled default empty
22330 * @cfg {Boolean} singleMode default false (true | false)
22332 * @cfg {Boolean} keyboardNavigation default true
22333 * @cfg {String} language default en
22336 * Create a new DateField
22337 * @param {Object} config The config object
22340 Roo.bootstrap.DateField = function(config){
22341 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22345 * Fires when this field show.
22346 * @param {Roo.bootstrap.DateField} this
22347 * @param {Mixed} date The date value
22352 * Fires when this field hide.
22353 * @param {Roo.bootstrap.DateField} this
22354 * @param {Mixed} date The date value
22359 * Fires when select a date.
22360 * @param {Roo.bootstrap.DateField} this
22361 * @param {Mixed} date The date value
22365 * @event beforeselect
22366 * Fires when before select a date.
22367 * @param {Roo.bootstrap.DateField} this
22368 * @param {Mixed} date The date value
22370 beforeselect : true
22374 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22377 * @cfg {String} format
22378 * The default date format string which can be overriden for localization support. The format must be
22379 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22383 * @cfg {String} altFormats
22384 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22385 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22387 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22395 todayHighlight : false,
22401 keyboardNavigation: true,
22403 calendarWeeks: false,
22405 startDate: -Infinity,
22409 daysOfWeekDisabled: [],
22413 singleMode : false,
22415 UTCDate: function()
22417 return new Date(Date.UTC.apply(Date, arguments));
22420 UTCToday: function()
22422 var today = new Date();
22423 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22426 getDate: function() {
22427 var d = this.getUTCDate();
22428 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22431 getUTCDate: function() {
22435 setDate: function(d) {
22436 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22439 setUTCDate: function(d) {
22441 this.setValue(this.formatDate(this.date));
22444 onRender: function(ct, position)
22447 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22449 this.language = this.language || 'en';
22450 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22451 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22453 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22454 this.format = this.format || 'm/d/y';
22455 this.isInline = false;
22456 this.isInput = true;
22457 this.component = this.el.select('.add-on', true).first() || false;
22458 this.component = (this.component && this.component.length === 0) ? false : this.component;
22459 this.hasInput = this.component && this.inputEl().length;
22461 if (typeof(this.minViewMode === 'string')) {
22462 switch (this.minViewMode) {
22464 this.minViewMode = 1;
22467 this.minViewMode = 2;
22470 this.minViewMode = 0;
22475 if (typeof(this.viewMode === 'string')) {
22476 switch (this.viewMode) {
22489 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22491 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22493 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22495 this.picker().on('mousedown', this.onMousedown, this);
22496 this.picker().on('click', this.onClick, this);
22498 this.picker().addClass('datepicker-dropdown');
22500 this.startViewMode = this.viewMode;
22502 if(this.singleMode){
22503 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22504 v.setVisibilityMode(Roo.Element.DISPLAY);
22508 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22509 v.setStyle('width', '189px');
22513 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22514 if(!this.calendarWeeks){
22519 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22520 v.attr('colspan', function(i, val){
22521 return parseInt(val) + 1;
22526 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22528 this.setStartDate(this.startDate);
22529 this.setEndDate(this.endDate);
22531 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22538 if(this.isInline) {
22543 picker : function()
22545 return this.pickerEl;
22546 // return this.el.select('.datepicker', true).first();
22549 fillDow: function()
22551 var dowCnt = this.weekStart;
22560 if(this.calendarWeeks){
22568 while (dowCnt < this.weekStart + 7) {
22572 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22576 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22579 fillMonths: function()
22582 var months = this.picker().select('>.datepicker-months td', true).first();
22584 months.dom.innerHTML = '';
22590 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22593 months.createChild(month);
22600 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;
22602 if (this.date < this.startDate) {
22603 this.viewDate = new Date(this.startDate);
22604 } else if (this.date > this.endDate) {
22605 this.viewDate = new Date(this.endDate);
22607 this.viewDate = new Date(this.date);
22615 var d = new Date(this.viewDate),
22616 year = d.getUTCFullYear(),
22617 month = d.getUTCMonth(),
22618 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22619 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22620 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22621 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22622 currentDate = this.date && this.date.valueOf(),
22623 today = this.UTCToday();
22625 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22627 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22629 // this.picker.select('>tfoot th.today').
22630 // .text(dates[this.language].today)
22631 // .toggle(this.todayBtn !== false);
22633 this.updateNavArrows();
22636 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22638 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22640 prevMonth.setUTCDate(day);
22642 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22644 var nextMonth = new Date(prevMonth);
22646 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22648 nextMonth = nextMonth.valueOf();
22650 var fillMonths = false;
22652 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22654 while(prevMonth.valueOf() <= nextMonth) {
22657 if (prevMonth.getUTCDay() === this.weekStart) {
22659 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22667 if(this.calendarWeeks){
22668 // ISO 8601: First week contains first thursday.
22669 // ISO also states week starts on Monday, but we can be more abstract here.
22671 // Start of current week: based on weekstart/current date
22672 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22673 // Thursday of this week
22674 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22675 // First Thursday of year, year from thursday
22676 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22677 // Calendar week: ms between thursdays, div ms per day, div 7 days
22678 calWeek = (th - yth) / 864e5 / 7 + 1;
22680 fillMonths.cn.push({
22688 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22690 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22693 if (this.todayHighlight &&
22694 prevMonth.getUTCFullYear() == today.getFullYear() &&
22695 prevMonth.getUTCMonth() == today.getMonth() &&
22696 prevMonth.getUTCDate() == today.getDate()) {
22697 clsName += ' today';
22700 if (currentDate && prevMonth.valueOf() === currentDate) {
22701 clsName += ' active';
22704 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22705 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22706 clsName += ' disabled';
22709 fillMonths.cn.push({
22711 cls: 'day ' + clsName,
22712 html: prevMonth.getDate()
22715 prevMonth.setDate(prevMonth.getDate()+1);
22718 var currentYear = this.date && this.date.getUTCFullYear();
22719 var currentMonth = this.date && this.date.getUTCMonth();
22721 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22723 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22724 v.removeClass('active');
22726 if(currentYear === year && k === currentMonth){
22727 v.addClass('active');
22730 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22731 v.addClass('disabled');
22737 year = parseInt(year/10, 10) * 10;
22739 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22741 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22744 for (var i = -1; i < 11; i++) {
22745 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22747 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22755 showMode: function(dir)
22758 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22761 Roo.each(this.picker().select('>div',true).elements, function(v){
22762 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22765 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22770 if(this.isInline) {
22774 this.picker().removeClass(['bottom', 'top']);
22776 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22778 * place to the top of element!
22782 this.picker().addClass('top');
22783 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22788 this.picker().addClass('bottom');
22790 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22793 parseDate : function(value)
22795 if(!value || value instanceof Date){
22798 var v = Date.parseDate(value, this.format);
22799 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22800 v = Date.parseDate(value, 'Y-m-d');
22802 if(!v && this.altFormats){
22803 if(!this.altFormatsArray){
22804 this.altFormatsArray = this.altFormats.split("|");
22806 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22807 v = Date.parseDate(value, this.altFormatsArray[i]);
22813 formatDate : function(date, fmt)
22815 return (!date || !(date instanceof Date)) ?
22816 date : date.dateFormat(fmt || this.format);
22819 onFocus : function()
22821 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22825 onBlur : function()
22827 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22829 var d = this.inputEl().getValue();
22836 showPopup : function()
22838 this.picker().show();
22842 this.fireEvent('showpopup', this, this.date);
22845 hidePopup : function()
22847 if(this.isInline) {
22850 this.picker().hide();
22851 this.viewMode = this.startViewMode;
22854 this.fireEvent('hidepopup', this, this.date);
22858 onMousedown: function(e)
22860 e.stopPropagation();
22861 e.preventDefault();
22866 Roo.bootstrap.DateField.superclass.keyup.call(this);
22870 setValue: function(v)
22872 if(this.fireEvent('beforeselect', this, v) !== false){
22873 var d = new Date(this.parseDate(v) ).clearTime();
22875 if(isNaN(d.getTime())){
22876 this.date = this.viewDate = '';
22877 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22881 v = this.formatDate(d);
22883 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22885 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22889 this.fireEvent('select', this, this.date);
22893 getValue: function()
22895 return this.formatDate(this.date);
22898 fireKey: function(e)
22900 if (!this.picker().isVisible()){
22901 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22907 var dateChanged = false,
22909 newDate, newViewDate;
22914 e.preventDefault();
22918 if (!this.keyboardNavigation) {
22921 dir = e.keyCode == 37 ? -1 : 1;
22924 newDate = this.moveYear(this.date, dir);
22925 newViewDate = this.moveYear(this.viewDate, dir);
22926 } else if (e.shiftKey){
22927 newDate = this.moveMonth(this.date, dir);
22928 newViewDate = this.moveMonth(this.viewDate, dir);
22930 newDate = new Date(this.date);
22931 newDate.setUTCDate(this.date.getUTCDate() + dir);
22932 newViewDate = new Date(this.viewDate);
22933 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22935 if (this.dateWithinRange(newDate)){
22936 this.date = newDate;
22937 this.viewDate = newViewDate;
22938 this.setValue(this.formatDate(this.date));
22940 e.preventDefault();
22941 dateChanged = true;
22946 if (!this.keyboardNavigation) {
22949 dir = e.keyCode == 38 ? -1 : 1;
22951 newDate = this.moveYear(this.date, dir);
22952 newViewDate = this.moveYear(this.viewDate, dir);
22953 } else if (e.shiftKey){
22954 newDate = this.moveMonth(this.date, dir);
22955 newViewDate = this.moveMonth(this.viewDate, dir);
22957 newDate = new Date(this.date);
22958 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22959 newViewDate = new Date(this.viewDate);
22960 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22962 if (this.dateWithinRange(newDate)){
22963 this.date = newDate;
22964 this.viewDate = newViewDate;
22965 this.setValue(this.formatDate(this.date));
22967 e.preventDefault();
22968 dateChanged = true;
22972 this.setValue(this.formatDate(this.date));
22974 e.preventDefault();
22977 this.setValue(this.formatDate(this.date));
22991 onClick: function(e)
22993 e.stopPropagation();
22994 e.preventDefault();
22996 var target = e.getTarget();
22998 if(target.nodeName.toLowerCase() === 'i'){
22999 target = Roo.get(target).dom.parentNode;
23002 var nodeName = target.nodeName;
23003 var className = target.className;
23004 var html = target.innerHTML;
23005 //Roo.log(nodeName);
23007 switch(nodeName.toLowerCase()) {
23009 switch(className) {
23015 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23016 switch(this.viewMode){
23018 this.viewDate = this.moveMonth(this.viewDate, dir);
23022 this.viewDate = this.moveYear(this.viewDate, dir);
23028 var date = new Date();
23029 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23031 this.setValue(this.formatDate(this.date));
23038 if (className.indexOf('disabled') < 0) {
23039 if (!this.viewDate) {
23040 this.viewDate = new Date();
23042 this.viewDate.setUTCDate(1);
23043 if (className.indexOf('month') > -1) {
23044 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23046 var year = parseInt(html, 10) || 0;
23047 this.viewDate.setUTCFullYear(year);
23051 if(this.singleMode){
23052 this.setValue(this.formatDate(this.viewDate));
23063 //Roo.log(className);
23064 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23065 var day = parseInt(html, 10) || 1;
23066 var year = (this.viewDate || new Date()).getUTCFullYear(),
23067 month = (this.viewDate || new Date()).getUTCMonth();
23069 if (className.indexOf('old') > -1) {
23076 } else if (className.indexOf('new') > -1) {
23084 //Roo.log([year,month,day]);
23085 this.date = this.UTCDate(year, month, day,0,0,0,0);
23086 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23088 //Roo.log(this.formatDate(this.date));
23089 this.setValue(this.formatDate(this.date));
23096 setStartDate: function(startDate)
23098 this.startDate = startDate || -Infinity;
23099 if (this.startDate !== -Infinity) {
23100 this.startDate = this.parseDate(this.startDate);
23103 this.updateNavArrows();
23106 setEndDate: function(endDate)
23108 this.endDate = endDate || Infinity;
23109 if (this.endDate !== Infinity) {
23110 this.endDate = this.parseDate(this.endDate);
23113 this.updateNavArrows();
23116 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23118 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23119 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23120 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23122 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23123 return parseInt(d, 10);
23126 this.updateNavArrows();
23129 updateNavArrows: function()
23131 if(this.singleMode){
23135 var d = new Date(this.viewDate),
23136 year = d.getUTCFullYear(),
23137 month = d.getUTCMonth();
23139 Roo.each(this.picker().select('.prev', true).elements, function(v){
23141 switch (this.viewMode) {
23144 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23150 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23157 Roo.each(this.picker().select('.next', true).elements, function(v){
23159 switch (this.viewMode) {
23162 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23168 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23176 moveMonth: function(date, dir)
23181 var new_date = new Date(date.valueOf()),
23182 day = new_date.getUTCDate(),
23183 month = new_date.getUTCMonth(),
23184 mag = Math.abs(dir),
23186 dir = dir > 0 ? 1 : -1;
23189 // If going back one month, make sure month is not current month
23190 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23192 return new_date.getUTCMonth() == month;
23194 // If going forward one month, make sure month is as expected
23195 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23197 return new_date.getUTCMonth() != new_month;
23199 new_month = month + dir;
23200 new_date.setUTCMonth(new_month);
23201 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23202 if (new_month < 0 || new_month > 11) {
23203 new_month = (new_month + 12) % 12;
23206 // For magnitudes >1, move one month at a time...
23207 for (var i=0; i<mag; i++) {
23208 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23209 new_date = this.moveMonth(new_date, dir);
23211 // ...then reset the day, keeping it in the new month
23212 new_month = new_date.getUTCMonth();
23213 new_date.setUTCDate(day);
23215 return new_month != new_date.getUTCMonth();
23218 // Common date-resetting loop -- if date is beyond end of month, make it
23221 new_date.setUTCDate(--day);
23222 new_date.setUTCMonth(new_month);
23227 moveYear: function(date, dir)
23229 return this.moveMonth(date, dir*12);
23232 dateWithinRange: function(date)
23234 return date >= this.startDate && date <= this.endDate;
23240 this.picker().remove();
23243 validateValue : function(value)
23245 if(this.getVisibilityEl().hasClass('hidden')){
23249 if(value.length < 1) {
23250 if(this.allowBlank){
23256 if(value.length < this.minLength){
23259 if(value.length > this.maxLength){
23263 var vt = Roo.form.VTypes;
23264 if(!vt[this.vtype](value, this)){
23268 if(typeof this.validator == "function"){
23269 var msg = this.validator(value);
23275 if(this.regex && !this.regex.test(value)){
23279 if(typeof(this.parseDate(value)) == 'undefined'){
23283 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23287 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23297 this.date = this.viewDate = '';
23299 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23304 Roo.apply(Roo.bootstrap.DateField, {
23315 html: '<i class="fa fa-arrow-left"/>'
23325 html: '<i class="fa fa-arrow-right"/>'
23367 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23368 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23369 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23370 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23371 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23384 navFnc: 'FullYear',
23389 navFnc: 'FullYear',
23394 Roo.apply(Roo.bootstrap.DateField, {
23398 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23402 cls: 'datepicker-days',
23406 cls: 'table-condensed',
23408 Roo.bootstrap.DateField.head,
23412 Roo.bootstrap.DateField.footer
23419 cls: 'datepicker-months',
23423 cls: 'table-condensed',
23425 Roo.bootstrap.DateField.head,
23426 Roo.bootstrap.DateField.content,
23427 Roo.bootstrap.DateField.footer
23434 cls: 'datepicker-years',
23438 cls: 'table-condensed',
23440 Roo.bootstrap.DateField.head,
23441 Roo.bootstrap.DateField.content,
23442 Roo.bootstrap.DateField.footer
23461 * @class Roo.bootstrap.TimeField
23462 * @extends Roo.bootstrap.Input
23463 * Bootstrap DateField class
23467 * Create a new TimeField
23468 * @param {Object} config The config object
23471 Roo.bootstrap.TimeField = function(config){
23472 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23476 * Fires when this field show.
23477 * @param {Roo.bootstrap.DateField} thisthis
23478 * @param {Mixed} date The date value
23483 * Fires when this field hide.
23484 * @param {Roo.bootstrap.DateField} this
23485 * @param {Mixed} date The date value
23490 * Fires when select a date.
23491 * @param {Roo.bootstrap.DateField} this
23492 * @param {Mixed} date The date value
23498 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23501 * @cfg {String} format
23502 * The default time format string which can be overriden for localization support. The format must be
23503 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23507 getAutoCreate : function()
23509 this.after = '<i class="fa far fa-clock"></i>';
23510 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23514 onRender: function(ct, position)
23517 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23519 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23521 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23523 this.pop = this.picker().select('>.datepicker-time',true).first();
23524 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23526 this.picker().on('mousedown', this.onMousedown, this);
23527 this.picker().on('click', this.onClick, this);
23529 this.picker().addClass('datepicker-dropdown');
23534 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23535 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23536 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23537 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23538 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23539 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23543 fireKey: function(e){
23544 if (!this.picker().isVisible()){
23545 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23551 e.preventDefault();
23559 this.onTogglePeriod();
23562 this.onIncrementMinutes();
23565 this.onDecrementMinutes();
23574 onClick: function(e) {
23575 e.stopPropagation();
23576 e.preventDefault();
23579 picker : function()
23581 return this.pickerEl;
23584 fillTime: function()
23586 var time = this.pop.select('tbody', true).first();
23588 time.dom.innerHTML = '';
23603 cls: 'hours-up fa fas fa-chevron-up'
23623 cls: 'minutes-up fa fas fa-chevron-up'
23644 cls: 'timepicker-hour',
23659 cls: 'timepicker-minute',
23674 cls: 'btn btn-primary period',
23696 cls: 'hours-down fa fas fa-chevron-down'
23716 cls: 'minutes-down fa fas fa-chevron-down'
23734 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23741 var hours = this.time.getHours();
23742 var minutes = this.time.getMinutes();
23755 hours = hours - 12;
23759 hours = '0' + hours;
23763 minutes = '0' + minutes;
23766 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23767 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23768 this.pop.select('button', true).first().dom.innerHTML = period;
23774 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23776 var cls = ['bottom'];
23778 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23785 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23789 //this.picker().setXY(20000,20000);
23790 this.picker().addClass(cls.join('-'));
23794 Roo.each(cls, function(c){
23799 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23800 //_this.picker().setTop(_this.inputEl().getHeight());
23804 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23806 //_this.picker().setTop(0 - _this.picker().getHeight());
23811 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23815 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23823 onFocus : function()
23825 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23829 onBlur : function()
23831 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23837 this.picker().show();
23842 this.fireEvent('show', this, this.date);
23847 this.picker().hide();
23850 this.fireEvent('hide', this, this.date);
23853 setTime : function()
23856 this.setValue(this.time.format(this.format));
23858 this.fireEvent('select', this, this.date);
23863 onMousedown: function(e){
23864 e.stopPropagation();
23865 e.preventDefault();
23868 onIncrementHours: function()
23870 Roo.log('onIncrementHours');
23871 this.time = this.time.add(Date.HOUR, 1);
23876 onDecrementHours: function()
23878 Roo.log('onDecrementHours');
23879 this.time = this.time.add(Date.HOUR, -1);
23883 onIncrementMinutes: function()
23885 Roo.log('onIncrementMinutes');
23886 this.time = this.time.add(Date.MINUTE, 1);
23890 onDecrementMinutes: function()
23892 Roo.log('onDecrementMinutes');
23893 this.time = this.time.add(Date.MINUTE, -1);
23897 onTogglePeriod: function()
23899 Roo.log('onTogglePeriod');
23900 this.time = this.time.add(Date.HOUR, 12);
23908 Roo.apply(Roo.bootstrap.TimeField, {
23912 cls: 'datepicker dropdown-menu',
23916 cls: 'datepicker-time',
23920 cls: 'table-condensed',
23949 cls: 'btn btn-info ok',
23977 * @class Roo.bootstrap.MonthField
23978 * @extends Roo.bootstrap.Input
23979 * Bootstrap MonthField class
23981 * @cfg {String} language default en
23984 * Create a new MonthField
23985 * @param {Object} config The config object
23988 Roo.bootstrap.MonthField = function(config){
23989 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23994 * Fires when this field show.
23995 * @param {Roo.bootstrap.MonthField} this
23996 * @param {Mixed} date The date value
24001 * Fires when this field hide.
24002 * @param {Roo.bootstrap.MonthField} this
24003 * @param {Mixed} date The date value
24008 * Fires when select a date.
24009 * @param {Roo.bootstrap.MonthField} this
24010 * @param {String} oldvalue The old value
24011 * @param {String} newvalue The new value
24017 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
24019 onRender: function(ct, position)
24022 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24024 this.language = this.language || 'en';
24025 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24026 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24028 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24029 this.isInline = false;
24030 this.isInput = true;
24031 this.component = this.el.select('.add-on', true).first() || false;
24032 this.component = (this.component && this.component.length === 0) ? false : this.component;
24033 this.hasInput = this.component && this.inputEL().length;
24035 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24037 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24039 this.picker().on('mousedown', this.onMousedown, this);
24040 this.picker().on('click', this.onClick, this);
24042 this.picker().addClass('datepicker-dropdown');
24044 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24045 v.setStyle('width', '189px');
24052 if(this.isInline) {
24058 setValue: function(v, suppressEvent)
24060 var o = this.getValue();
24062 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24066 if(suppressEvent !== true){
24067 this.fireEvent('select', this, o, v);
24072 getValue: function()
24077 onClick: function(e)
24079 e.stopPropagation();
24080 e.preventDefault();
24082 var target = e.getTarget();
24084 if(target.nodeName.toLowerCase() === 'i'){
24085 target = Roo.get(target).dom.parentNode;
24088 var nodeName = target.nodeName;
24089 var className = target.className;
24090 var html = target.innerHTML;
24092 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24096 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24098 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24104 picker : function()
24106 return this.pickerEl;
24109 fillMonths: function()
24112 var months = this.picker().select('>.datepicker-months td', true).first();
24114 months.dom.innerHTML = '';
24120 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24123 months.createChild(month);
24132 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24133 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24136 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24137 e.removeClass('active');
24139 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24140 e.addClass('active');
24147 if(this.isInline) {
24151 this.picker().removeClass(['bottom', 'top']);
24153 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24155 * place to the top of element!
24159 this.picker().addClass('top');
24160 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24165 this.picker().addClass('bottom');
24167 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24170 onFocus : function()
24172 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24176 onBlur : function()
24178 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24180 var d = this.inputEl().getValue();
24189 this.picker().show();
24190 this.picker().select('>.datepicker-months', true).first().show();
24194 this.fireEvent('show', this, this.date);
24199 if(this.isInline) {
24202 this.picker().hide();
24203 this.fireEvent('hide', this, this.date);
24207 onMousedown: function(e)
24209 e.stopPropagation();
24210 e.preventDefault();
24215 Roo.bootstrap.MonthField.superclass.keyup.call(this);
24219 fireKey: function(e)
24221 if (!this.picker().isVisible()){
24222 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24233 e.preventDefault();
24237 dir = e.keyCode == 37 ? -1 : 1;
24239 this.vIndex = this.vIndex + dir;
24241 if(this.vIndex < 0){
24245 if(this.vIndex > 11){
24249 if(isNaN(this.vIndex)){
24253 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24259 dir = e.keyCode == 38 ? -1 : 1;
24261 this.vIndex = this.vIndex + dir * 4;
24263 if(this.vIndex < 0){
24267 if(this.vIndex > 11){
24271 if(isNaN(this.vIndex)){
24275 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24280 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24281 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24285 e.preventDefault();
24288 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24289 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24305 this.picker().remove();
24310 Roo.apply(Roo.bootstrap.MonthField, {
24329 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24330 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24335 Roo.apply(Roo.bootstrap.MonthField, {
24339 cls: 'datepicker dropdown-menu roo-dynamic',
24343 cls: 'datepicker-months',
24347 cls: 'table-condensed',
24349 Roo.bootstrap.DateField.content
24369 * @class Roo.bootstrap.CheckBox
24370 * @extends Roo.bootstrap.Input
24371 * Bootstrap CheckBox class
24373 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24374 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24375 * @cfg {String} boxLabel The text that appears beside the checkbox
24376 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24377 * @cfg {Boolean} checked initnal the element
24378 * @cfg {Boolean} inline inline the element (default false)
24379 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24380 * @cfg {String} tooltip label tooltip
24383 * Create a new CheckBox
24384 * @param {Object} config The config object
24387 Roo.bootstrap.CheckBox = function(config){
24388 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24393 * Fires when the element is checked or unchecked.
24394 * @param {Roo.bootstrap.CheckBox} this This input
24395 * @param {Boolean} checked The new checked value
24400 * Fires when the element is click.
24401 * @param {Roo.bootstrap.CheckBox} this This input
24408 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24410 inputType: 'checkbox',
24419 // checkbox success does not make any sense really..
24424 getAutoCreate : function()
24426 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24432 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24435 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24441 type : this.inputType,
24442 value : this.inputValue,
24443 cls : 'roo-' + this.inputType, //'form-box',
24444 placeholder : this.placeholder || ''
24448 if(this.inputType != 'radio'){
24452 cls : 'roo-hidden-value',
24453 value : this.checked ? this.inputValue : this.valueOff
24458 if (this.weight) { // Validity check?
24459 cfg.cls += " " + this.inputType + "-" + this.weight;
24462 if (this.disabled) {
24463 input.disabled=true;
24467 input.checked = this.checked;
24472 input.name = this.name;
24474 if(this.inputType != 'radio'){
24475 hidden.name = this.name;
24476 input.name = '_hidden_' + this.name;
24481 input.cls += ' input-' + this.size;
24486 ['xs','sm','md','lg'].map(function(size){
24487 if (settings[size]) {
24488 cfg.cls += ' col-' + size + '-' + settings[size];
24492 var inputblock = input;
24494 if (this.before || this.after) {
24497 cls : 'input-group',
24502 inputblock.cn.push({
24504 cls : 'input-group-addon',
24509 inputblock.cn.push(input);
24511 if(this.inputType != 'radio'){
24512 inputblock.cn.push(hidden);
24516 inputblock.cn.push({
24518 cls : 'input-group-addon',
24524 var boxLabelCfg = false;
24530 //'for': id, // box label is handled by onclick - so no for...
24532 html: this.boxLabel
24535 boxLabelCfg.tooltip = this.tooltip;
24541 if (align ==='left' && this.fieldLabel.length) {
24542 // Roo.log("left and has label");
24547 cls : 'control-label',
24548 html : this.fieldLabel
24559 cfg.cn[1].cn.push(boxLabelCfg);
24562 if(this.labelWidth > 12){
24563 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24566 if(this.labelWidth < 13 && this.labelmd == 0){
24567 this.labelmd = this.labelWidth;
24570 if(this.labellg > 0){
24571 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24572 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24575 if(this.labelmd > 0){
24576 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24577 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24580 if(this.labelsm > 0){
24581 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24582 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24585 if(this.labelxs > 0){
24586 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24587 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24590 } else if ( this.fieldLabel.length) {
24591 // Roo.log(" label");
24595 tag: this.boxLabel ? 'span' : 'label',
24597 cls: 'control-label box-input-label',
24598 //cls : 'input-group-addon',
24599 html : this.fieldLabel
24606 cfg.cn.push(boxLabelCfg);
24611 // Roo.log(" no label && no align");
24612 cfg.cn = [ inputblock ] ;
24614 cfg.cn.push(boxLabelCfg);
24622 if(this.inputType != 'radio'){
24623 cfg.cn.push(hidden);
24631 * return the real input element.
24633 inputEl: function ()
24635 return this.el.select('input.roo-' + this.inputType,true).first();
24637 hiddenEl: function ()
24639 return this.el.select('input.roo-hidden-value',true).first();
24642 labelEl: function()
24644 return this.el.select('label.control-label',true).first();
24646 /* depricated... */
24650 return this.labelEl();
24653 boxLabelEl: function()
24655 return this.el.select('label.box-label',true).first();
24658 initEvents : function()
24660 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24662 this.inputEl().on('click', this.onClick, this);
24664 if (this.boxLabel) {
24665 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24668 this.startValue = this.getValue();
24671 Roo.bootstrap.CheckBox.register(this);
24675 onClick : function(e)
24677 if(this.fireEvent('click', this, e) !== false){
24678 this.setChecked(!this.checked);
24683 setChecked : function(state,suppressEvent)
24685 this.startValue = this.getValue();
24687 if(this.inputType == 'radio'){
24689 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24690 e.dom.checked = false;
24693 this.inputEl().dom.checked = true;
24695 this.inputEl().dom.value = this.inputValue;
24697 if(suppressEvent !== true){
24698 this.fireEvent('check', this, true);
24706 this.checked = state;
24708 this.inputEl().dom.checked = state;
24711 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24713 if(suppressEvent !== true){
24714 this.fireEvent('check', this, state);
24720 getValue : function()
24722 if(this.inputType == 'radio'){
24723 return this.getGroupValue();
24726 return this.hiddenEl().dom.value;
24730 getGroupValue : function()
24732 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24736 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24739 setValue : function(v,suppressEvent)
24741 if(this.inputType == 'radio'){
24742 this.setGroupValue(v, suppressEvent);
24746 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24751 setGroupValue : function(v, suppressEvent)
24753 this.startValue = this.getValue();
24755 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24756 e.dom.checked = false;
24758 if(e.dom.value == v){
24759 e.dom.checked = true;
24763 if(suppressEvent !== true){
24764 this.fireEvent('check', this, true);
24772 validate : function()
24774 if(this.getVisibilityEl().hasClass('hidden')){
24780 (this.inputType == 'radio' && this.validateRadio()) ||
24781 (this.inputType == 'checkbox' && this.validateCheckbox())
24787 this.markInvalid();
24791 validateRadio : function()
24793 if(this.getVisibilityEl().hasClass('hidden')){
24797 if(this.allowBlank){
24803 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24804 if(!e.dom.checked){
24816 validateCheckbox : function()
24819 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24820 //return (this.getValue() == this.inputValue) ? true : false;
24823 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24831 for(var i in group){
24832 if(group[i].el.isVisible(true)){
24840 for(var i in group){
24845 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24852 * Mark this field as valid
24854 markValid : function()
24858 this.fireEvent('valid', this);
24860 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24863 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24870 if(this.inputType == 'radio'){
24871 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24872 var fg = e.findParent('.form-group', false, true);
24873 if (Roo.bootstrap.version == 3) {
24874 fg.removeClass([_this.invalidClass, _this.validClass]);
24875 fg.addClass(_this.validClass);
24877 fg.removeClass(['is-valid', 'is-invalid']);
24878 fg.addClass('is-valid');
24886 var fg = this.el.findParent('.form-group', false, true);
24887 if (Roo.bootstrap.version == 3) {
24888 fg.removeClass([this.invalidClass, this.validClass]);
24889 fg.addClass(this.validClass);
24891 fg.removeClass(['is-valid', 'is-invalid']);
24892 fg.addClass('is-valid');
24897 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24903 for(var i in group){
24904 var fg = group[i].el.findParent('.form-group', false, true);
24905 if (Roo.bootstrap.version == 3) {
24906 fg.removeClass([this.invalidClass, this.validClass]);
24907 fg.addClass(this.validClass);
24909 fg.removeClass(['is-valid', 'is-invalid']);
24910 fg.addClass('is-valid');
24916 * Mark this field as invalid
24917 * @param {String} msg The validation message
24919 markInvalid : function(msg)
24921 if(this.allowBlank){
24927 this.fireEvent('invalid', this, msg);
24929 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24932 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24936 label.markInvalid();
24939 if(this.inputType == 'radio'){
24941 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24942 var fg = e.findParent('.form-group', false, true);
24943 if (Roo.bootstrap.version == 3) {
24944 fg.removeClass([_this.invalidClass, _this.validClass]);
24945 fg.addClass(_this.invalidClass);
24947 fg.removeClass(['is-invalid', 'is-valid']);
24948 fg.addClass('is-invalid');
24956 var fg = this.el.findParent('.form-group', false, true);
24957 if (Roo.bootstrap.version == 3) {
24958 fg.removeClass([_this.invalidClass, _this.validClass]);
24959 fg.addClass(_this.invalidClass);
24961 fg.removeClass(['is-invalid', 'is-valid']);
24962 fg.addClass('is-invalid');
24967 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24973 for(var i in group){
24974 var fg = group[i].el.findParent('.form-group', false, true);
24975 if (Roo.bootstrap.version == 3) {
24976 fg.removeClass([_this.invalidClass, _this.validClass]);
24977 fg.addClass(_this.invalidClass);
24979 fg.removeClass(['is-invalid', 'is-valid']);
24980 fg.addClass('is-invalid');
24986 clearInvalid : function()
24988 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24990 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24992 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24994 if (label && label.iconEl) {
24995 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24996 label.iconEl.removeClass(['is-invalid', 'is-valid']);
25000 disable : function()
25002 if(this.inputType != 'radio'){
25003 Roo.bootstrap.CheckBox.superclass.disable.call(this);
25010 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25011 _this.getActionEl().addClass(this.disabledClass);
25012 e.dom.disabled = true;
25016 this.disabled = true;
25017 this.fireEvent("disable", this);
25021 enable : function()
25023 if(this.inputType != 'radio'){
25024 Roo.bootstrap.CheckBox.superclass.enable.call(this);
25031 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25032 _this.getActionEl().removeClass(this.disabledClass);
25033 e.dom.disabled = false;
25037 this.disabled = false;
25038 this.fireEvent("enable", this);
25042 setBoxLabel : function(v)
25047 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25053 Roo.apply(Roo.bootstrap.CheckBox, {
25058 * register a CheckBox Group
25059 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25061 register : function(checkbox)
25063 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25064 this.groups[checkbox.groupId] = {};
25067 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25071 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25075 * fetch a CheckBox Group based on the group ID
25076 * @param {string} the group ID
25077 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25079 get: function(groupId) {
25080 if (typeof(this.groups[groupId]) == 'undefined') {
25084 return this.groups[groupId] ;
25097 * @class Roo.bootstrap.Radio
25098 * @extends Roo.bootstrap.Component
25099 * Bootstrap Radio class
25100 * @cfg {String} boxLabel - the label associated
25101 * @cfg {String} value - the value of radio
25104 * Create a new Radio
25105 * @param {Object} config The config object
25107 Roo.bootstrap.Radio = function(config){
25108 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25112 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25118 getAutoCreate : function()
25122 cls : 'form-group radio',
25127 html : this.boxLabel
25135 initEvents : function()
25137 this.parent().register(this);
25139 this.el.on('click', this.onClick, this);
25143 onClick : function(e)
25145 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25146 this.setChecked(true);
25150 setChecked : function(state, suppressEvent)
25152 this.parent().setValue(this.value, suppressEvent);
25156 setBoxLabel : function(v)
25161 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25176 * @class Roo.bootstrap.SecurePass
25177 * @extends Roo.bootstrap.Input
25178 * Bootstrap SecurePass class
25182 * Create a new SecurePass
25183 * @param {Object} config The config object
25186 Roo.bootstrap.SecurePass = function (config) {
25187 // these go here, so the translation tool can replace them..
25189 PwdEmpty: "Please type a password, and then retype it to confirm.",
25190 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25191 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25192 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25193 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25194 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25195 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25196 TooWeak: "Your password is Too Weak."
25198 this.meterLabel = "Password strength:";
25199 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25200 this.meterClass = [
25201 "roo-password-meter-tooweak",
25202 "roo-password-meter-weak",
25203 "roo-password-meter-medium",
25204 "roo-password-meter-strong",
25205 "roo-password-meter-grey"
25210 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25213 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25215 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25217 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25218 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25219 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25220 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25221 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25222 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25223 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25233 * @cfg {String/Object} Label for the strength meter (defaults to
25234 * 'Password strength:')
25239 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25240 * ['Weak', 'Medium', 'Strong'])
25243 pwdStrengths: false,
25256 initEvents: function ()
25258 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25260 if (this.el.is('input[type=password]') && Roo.isSafari) {
25261 this.el.on('keydown', this.SafariOnKeyDown, this);
25264 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25267 onRender: function (ct, position)
25269 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25270 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25271 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25273 this.trigger.createChild({
25278 cls: 'roo-password-meter-grey col-xs-12',
25281 //width: this.meterWidth + 'px'
25285 cls: 'roo-password-meter-text'
25291 if (this.hideTrigger) {
25292 this.trigger.setDisplayed(false);
25294 this.setSize(this.width || '', this.height || '');
25297 onDestroy: function ()
25299 if (this.trigger) {
25300 this.trigger.removeAllListeners();
25301 this.trigger.remove();
25304 this.wrap.remove();
25306 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25309 checkStrength: function ()
25311 var pwd = this.inputEl().getValue();
25312 if (pwd == this._lastPwd) {
25317 if (this.ClientSideStrongPassword(pwd)) {
25319 } else if (this.ClientSideMediumPassword(pwd)) {
25321 } else if (this.ClientSideWeakPassword(pwd)) {
25327 Roo.log('strength1: ' + strength);
25329 //var pm = this.trigger.child('div/div/div').dom;
25330 var pm = this.trigger.child('div/div');
25331 pm.removeClass(this.meterClass);
25332 pm.addClass(this.meterClass[strength]);
25335 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25337 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25339 this._lastPwd = pwd;
25343 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25345 this._lastPwd = '';
25347 var pm = this.trigger.child('div/div');
25348 pm.removeClass(this.meterClass);
25349 pm.addClass('roo-password-meter-grey');
25352 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25355 this.inputEl().dom.type='password';
25358 validateValue: function (value)
25360 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25363 if (value.length == 0) {
25364 if (this.allowBlank) {
25365 this.clearInvalid();
25369 this.markInvalid(this.errors.PwdEmpty);
25370 this.errorMsg = this.errors.PwdEmpty;
25378 if (!value.match(/[\x21-\x7e]+/)) {
25379 this.markInvalid(this.errors.PwdBadChar);
25380 this.errorMsg = this.errors.PwdBadChar;
25383 if (value.length < 6) {
25384 this.markInvalid(this.errors.PwdShort);
25385 this.errorMsg = this.errors.PwdShort;
25388 if (value.length > 16) {
25389 this.markInvalid(this.errors.PwdLong);
25390 this.errorMsg = this.errors.PwdLong;
25394 if (this.ClientSideStrongPassword(value)) {
25396 } else if (this.ClientSideMediumPassword(value)) {
25398 } else if (this.ClientSideWeakPassword(value)) {
25405 if (strength < 2) {
25406 //this.markInvalid(this.errors.TooWeak);
25407 this.errorMsg = this.errors.TooWeak;
25412 console.log('strength2: ' + strength);
25414 //var pm = this.trigger.child('div/div/div').dom;
25416 var pm = this.trigger.child('div/div');
25417 pm.removeClass(this.meterClass);
25418 pm.addClass(this.meterClass[strength]);
25420 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25422 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25424 this.errorMsg = '';
25428 CharacterSetChecks: function (type)
25431 this.fResult = false;
25434 isctype: function (character, type)
25437 case this.kCapitalLetter:
25438 if (character >= 'A' && character <= 'Z') {
25443 case this.kSmallLetter:
25444 if (character >= 'a' && character <= 'z') {
25450 if (character >= '0' && character <= '9') {
25455 case this.kPunctuation:
25456 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25467 IsLongEnough: function (pwd, size)
25469 return !(pwd == null || isNaN(size) || pwd.length < size);
25472 SpansEnoughCharacterSets: function (word, nb)
25474 if (!this.IsLongEnough(word, nb))
25479 var characterSetChecks = new Array(
25480 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25481 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25484 for (var index = 0; index < word.length; ++index) {
25485 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25486 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25487 characterSetChecks[nCharSet].fResult = true;
25494 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25495 if (characterSetChecks[nCharSet].fResult) {
25500 if (nCharSets < nb) {
25506 ClientSideStrongPassword: function (pwd)
25508 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25511 ClientSideMediumPassword: function (pwd)
25513 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25516 ClientSideWeakPassword: function (pwd)
25518 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25521 })//<script type="text/javascript">
25524 * Based Ext JS Library 1.1.1
25525 * Copyright(c) 2006-2007, Ext JS, LLC.
25531 * @class Roo.HtmlEditorCore
25532 * @extends Roo.Component
25533 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25535 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25538 Roo.HtmlEditorCore = function(config){
25541 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25546 * @event initialize
25547 * Fires when the editor is fully initialized (including the iframe)
25548 * @param {Roo.HtmlEditorCore} this
25553 * Fires when the editor is first receives the focus. Any insertion must wait
25554 * until after this event.
25555 * @param {Roo.HtmlEditorCore} this
25559 * @event beforesync
25560 * Fires before the textarea is updated with content from the editor iframe. Return false
25561 * to cancel the sync.
25562 * @param {Roo.HtmlEditorCore} this
25563 * @param {String} html
25567 * @event beforepush
25568 * Fires before the iframe editor is updated with content from the textarea. Return false
25569 * to cancel the push.
25570 * @param {Roo.HtmlEditorCore} this
25571 * @param {String} html
25576 * Fires when the textarea is updated with content from the editor iframe.
25577 * @param {Roo.HtmlEditorCore} this
25578 * @param {String} html
25583 * Fires when the iframe editor is updated with content from the textarea.
25584 * @param {Roo.HtmlEditorCore} this
25585 * @param {String} html
25590 * @event editorevent
25591 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25592 * @param {Roo.HtmlEditorCore} this
25598 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25600 // defaults : white / black...
25601 this.applyBlacklists();
25608 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25612 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25618 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25623 * @cfg {Number} height (in pixels)
25627 * @cfg {Number} width (in pixels)
25632 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25635 stylesheets: false,
25638 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25640 allowComments: false,
25644 // private properties
25645 validationEvent : false,
25647 initialized : false,
25649 sourceEditMode : false,
25650 onFocus : Roo.emptyFn,
25652 hideMode:'offsets',
25656 // blacklist + whitelisted elements..
25663 * Protected method that will not generally be called directly. It
25664 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25665 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25667 getDocMarkup : function(){
25671 // inherit styels from page...??
25672 if (this.stylesheets === false) {
25674 Roo.get(document.head).select('style').each(function(node) {
25675 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25678 Roo.get(document.head).select('link').each(function(node) {
25679 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25682 } else if (!this.stylesheets.length) {
25684 st = '<style type="text/css">' +
25685 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25688 for (var i in this.stylesheets) {
25689 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25694 st += '<style type="text/css">' +
25695 'IMG { cursor: pointer } ' +
25698 var cls = 'roo-htmleditor-body';
25700 if(this.bodyCls.length){
25701 cls += ' ' + this.bodyCls;
25704 return '<html><head>' + st +
25705 //<style type="text/css">' +
25706 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25708 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25712 onRender : function(ct, position)
25715 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25716 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25719 this.el.dom.style.border = '0 none';
25720 this.el.dom.setAttribute('tabIndex', -1);
25721 this.el.addClass('x-hidden hide');
25725 if(Roo.isIE){ // fix IE 1px bogus margin
25726 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25730 this.frameId = Roo.id();
25734 var iframe = this.owner.wrap.createChild({
25736 cls: 'form-control', // bootstrap..
25738 name: this.frameId,
25739 frameBorder : 'no',
25740 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25745 this.iframe = iframe.dom;
25747 this.assignDocWin();
25749 this.doc.designMode = 'on';
25752 this.doc.write(this.getDocMarkup());
25756 var task = { // must defer to wait for browser to be ready
25758 //console.log("run task?" + this.doc.readyState);
25759 this.assignDocWin();
25760 if(this.doc.body || this.doc.readyState == 'complete'){
25762 this.doc.designMode="on";
25766 Roo.TaskMgr.stop(task);
25767 this.initEditor.defer(10, this);
25774 Roo.TaskMgr.start(task);
25779 onResize : function(w, h)
25781 Roo.log('resize: ' +w + ',' + h );
25782 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25786 if(typeof w == 'number'){
25788 this.iframe.style.width = w + 'px';
25790 if(typeof h == 'number'){
25792 this.iframe.style.height = h + 'px';
25794 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25801 * Toggles the editor between standard and source edit mode.
25802 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25804 toggleSourceEdit : function(sourceEditMode){
25806 this.sourceEditMode = sourceEditMode === true;
25808 if(this.sourceEditMode){
25810 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25813 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25814 //this.iframe.className = '';
25817 //this.setSize(this.owner.wrap.getSize());
25818 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25825 * Protected method that will not generally be called directly. If you need/want
25826 * custom HTML cleanup, this is the method you should override.
25827 * @param {String} html The HTML to be cleaned
25828 * return {String} The cleaned HTML
25830 cleanHtml : function(html){
25831 html = String(html);
25832 if(html.length > 5){
25833 if(Roo.isSafari){ // strip safari nonsense
25834 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25837 if(html == ' '){
25844 * HTML Editor -> Textarea
25845 * Protected method that will not generally be called directly. Syncs the contents
25846 * of the editor iframe with the textarea.
25848 syncValue : function(){
25849 if(this.initialized){
25850 var bd = (this.doc.body || this.doc.documentElement);
25851 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25852 var html = bd.innerHTML;
25854 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25855 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25857 html = '<div style="'+m[0]+'">' + html + '</div>';
25860 html = this.cleanHtml(html);
25861 // fix up the special chars.. normaly like back quotes in word...
25862 // however we do not want to do this with chinese..
25863 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25865 var cc = match.charCodeAt();
25867 // Get the character value, handling surrogate pairs
25868 if (match.length == 2) {
25869 // It's a surrogate pair, calculate the Unicode code point
25870 var high = match.charCodeAt(0) - 0xD800;
25871 var low = match.charCodeAt(1) - 0xDC00;
25872 cc = (high * 0x400) + low + 0x10000;
25874 (cc >= 0x4E00 && cc < 0xA000 ) ||
25875 (cc >= 0x3400 && cc < 0x4E00 ) ||
25876 (cc >= 0xf900 && cc < 0xfb00 )
25881 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25882 return "&#" + cc + ";";
25889 if(this.owner.fireEvent('beforesync', this, html) !== false){
25890 this.el.dom.value = html;
25891 this.owner.fireEvent('sync', this, html);
25897 * Protected method that will not generally be called directly. Pushes the value of the textarea
25898 * into the iframe editor.
25900 pushValue : function(){
25901 if(this.initialized){
25902 var v = this.el.dom.value.trim();
25904 // if(v.length < 1){
25908 if(this.owner.fireEvent('beforepush', this, v) !== false){
25909 var d = (this.doc.body || this.doc.documentElement);
25911 this.cleanUpPaste();
25912 this.el.dom.value = d.innerHTML;
25913 this.owner.fireEvent('push', this, v);
25919 deferFocus : function(){
25920 this.focus.defer(10, this);
25924 focus : function(){
25925 if(this.win && !this.sourceEditMode){
25932 assignDocWin: function()
25934 var iframe = this.iframe;
25937 this.doc = iframe.contentWindow.document;
25938 this.win = iframe.contentWindow;
25940 // if (!Roo.get(this.frameId)) {
25943 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25944 // this.win = Roo.get(this.frameId).dom.contentWindow;
25946 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25950 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25951 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25956 initEditor : function(){
25957 //console.log("INIT EDITOR");
25958 this.assignDocWin();
25962 this.doc.designMode="on";
25964 this.doc.write(this.getDocMarkup());
25967 var dbody = (this.doc.body || this.doc.documentElement);
25968 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25969 // this copies styles from the containing element into thsi one..
25970 // not sure why we need all of this..
25971 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25973 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25974 //ss['background-attachment'] = 'fixed'; // w3c
25975 dbody.bgProperties = 'fixed'; // ie
25976 //Roo.DomHelper.applyStyles(dbody, ss);
25977 Roo.EventManager.on(this.doc, {
25978 //'mousedown': this.onEditorEvent,
25979 'mouseup': this.onEditorEvent,
25980 'dblclick': this.onEditorEvent,
25981 'click': this.onEditorEvent,
25982 'keyup': this.onEditorEvent,
25987 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25989 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25990 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25992 this.initialized = true;
25994 this.owner.fireEvent('initialize', this);
25999 onDestroy : function(){
26005 //for (var i =0; i < this.toolbars.length;i++) {
26006 // // fixme - ask toolbars for heights?
26007 // this.toolbars[i].onDestroy();
26010 //this.wrap.dom.innerHTML = '';
26011 //this.wrap.remove();
26016 onFirstFocus : function(){
26018 this.assignDocWin();
26021 this.activated = true;
26024 if(Roo.isGecko){ // prevent silly gecko errors
26026 var s = this.win.getSelection();
26027 if(!s.focusNode || s.focusNode.nodeType != 3){
26028 var r = s.getRangeAt(0);
26029 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26034 this.execCmd('useCSS', true);
26035 this.execCmd('styleWithCSS', false);
26038 this.owner.fireEvent('activate', this);
26042 adjustFont: function(btn){
26043 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26044 //if(Roo.isSafari){ // safari
26047 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26048 if(Roo.isSafari){ // safari
26049 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26050 v = (v < 10) ? 10 : v;
26051 v = (v > 48) ? 48 : v;
26052 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26057 v = Math.max(1, v+adjust);
26059 this.execCmd('FontSize', v );
26062 onEditorEvent : function(e)
26064 this.owner.fireEvent('editorevent', this, e);
26065 // this.updateToolbar();
26066 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26069 insertTag : function(tg)
26071 // could be a bit smarter... -> wrap the current selected tRoo..
26072 if (tg.toLowerCase() == 'span' ||
26073 tg.toLowerCase() == 'code' ||
26074 tg.toLowerCase() == 'sup' ||
26075 tg.toLowerCase() == 'sub'
26078 range = this.createRange(this.getSelection());
26079 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26080 wrappingNode.appendChild(range.extractContents());
26081 range.insertNode(wrappingNode);
26088 this.execCmd("formatblock", tg);
26092 insertText : function(txt)
26096 var range = this.createRange();
26097 range.deleteContents();
26098 //alert(Sender.getAttribute('label'));
26100 range.insertNode(this.doc.createTextNode(txt));
26106 * Executes a Midas editor command on the editor document and performs necessary focus and
26107 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26108 * @param {String} cmd The Midas command
26109 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26111 relayCmd : function(cmd, value){
26113 this.execCmd(cmd, value);
26114 this.owner.fireEvent('editorevent', this);
26115 //this.updateToolbar();
26116 this.owner.deferFocus();
26120 * Executes a Midas editor command directly on the editor document.
26121 * For visual commands, you should use {@link #relayCmd} instead.
26122 * <b>This should only be called after the editor is initialized.</b>
26123 * @param {String} cmd The Midas command
26124 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26126 execCmd : function(cmd, value){
26127 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26134 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26136 * @param {String} text | dom node..
26138 insertAtCursor : function(text)
26141 if(!this.activated){
26147 var r = this.doc.selection.createRange();
26158 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26162 // from jquery ui (MIT licenced)
26164 var win = this.win;
26166 if (win.getSelection && win.getSelection().getRangeAt) {
26167 range = win.getSelection().getRangeAt(0);
26168 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26169 range.insertNode(node);
26170 } else if (win.document.selection && win.document.selection.createRange) {
26171 // no firefox support
26172 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26173 win.document.selection.createRange().pasteHTML(txt);
26175 // no firefox support
26176 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26177 this.execCmd('InsertHTML', txt);
26186 mozKeyPress : function(e){
26188 var c = e.getCharCode(), cmd;
26191 c = String.fromCharCode(c).toLowerCase();
26205 this.cleanUpPaste.defer(100, this);
26213 e.preventDefault();
26221 fixKeys : function(){ // load time branching for fastest keydown performance
26223 return function(e){
26224 var k = e.getKey(), r;
26227 r = this.doc.selection.createRange();
26230 r.pasteHTML('    ');
26237 r = this.doc.selection.createRange();
26239 var target = r.parentElement();
26240 if(!target || target.tagName.toLowerCase() != 'li'){
26242 r.pasteHTML('<br />');
26248 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26249 this.cleanUpPaste.defer(100, this);
26255 }else if(Roo.isOpera){
26256 return function(e){
26257 var k = e.getKey();
26261 this.execCmd('InsertHTML','    ');
26264 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26265 this.cleanUpPaste.defer(100, this);
26270 }else if(Roo.isSafari){
26271 return function(e){
26272 var k = e.getKey();
26276 this.execCmd('InsertText','\t');
26280 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26281 this.cleanUpPaste.defer(100, this);
26289 getAllAncestors: function()
26291 var p = this.getSelectedNode();
26294 a.push(p); // push blank onto stack..
26295 p = this.getParentElement();
26299 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26303 a.push(this.doc.body);
26307 lastSelNode : false,
26310 getSelection : function()
26312 this.assignDocWin();
26313 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26316 getSelectedNode: function()
26318 // this may only work on Gecko!!!
26320 // should we cache this!!!!
26325 var range = this.createRange(this.getSelection()).cloneRange();
26328 var parent = range.parentElement();
26330 var testRange = range.duplicate();
26331 testRange.moveToElementText(parent);
26332 if (testRange.inRange(range)) {
26335 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26338 parent = parent.parentElement;
26343 // is ancestor a text element.
26344 var ac = range.commonAncestorContainer;
26345 if (ac.nodeType == 3) {
26346 ac = ac.parentNode;
26349 var ar = ac.childNodes;
26352 var other_nodes = [];
26353 var has_other_nodes = false;
26354 for (var i=0;i<ar.length;i++) {
26355 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26358 // fullly contained node.
26360 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26365 // probably selected..
26366 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26367 other_nodes.push(ar[i]);
26371 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26376 has_other_nodes = true;
26378 if (!nodes.length && other_nodes.length) {
26379 nodes= other_nodes;
26381 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26387 createRange: function(sel)
26389 // this has strange effects when using with
26390 // top toolbar - not sure if it's a great idea.
26391 //this.editor.contentWindow.focus();
26392 if (typeof sel != "undefined") {
26394 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26396 return this.doc.createRange();
26399 return this.doc.createRange();
26402 getParentElement: function()
26405 this.assignDocWin();
26406 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26408 var range = this.createRange(sel);
26411 var p = range.commonAncestorContainer;
26412 while (p.nodeType == 3) { // text node
26423 * Range intersection.. the hard stuff...
26427 * [ -- selected range --- ]
26431 * if end is before start or hits it. fail.
26432 * if start is after end or hits it fail.
26434 * if either hits (but other is outside. - then it's not
26440 // @see http://www.thismuchiknow.co.uk/?p=64.
26441 rangeIntersectsNode : function(range, node)
26443 var nodeRange = node.ownerDocument.createRange();
26445 nodeRange.selectNode(node);
26447 nodeRange.selectNodeContents(node);
26450 var rangeStartRange = range.cloneRange();
26451 rangeStartRange.collapse(true);
26453 var rangeEndRange = range.cloneRange();
26454 rangeEndRange.collapse(false);
26456 var nodeStartRange = nodeRange.cloneRange();
26457 nodeStartRange.collapse(true);
26459 var nodeEndRange = nodeRange.cloneRange();
26460 nodeEndRange.collapse(false);
26462 return rangeStartRange.compareBoundaryPoints(
26463 Range.START_TO_START, nodeEndRange) == -1 &&
26464 rangeEndRange.compareBoundaryPoints(
26465 Range.START_TO_START, nodeStartRange) == 1;
26469 rangeCompareNode : function(range, node)
26471 var nodeRange = node.ownerDocument.createRange();
26473 nodeRange.selectNode(node);
26475 nodeRange.selectNodeContents(node);
26479 range.collapse(true);
26481 nodeRange.collapse(true);
26483 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26484 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26486 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26488 var nodeIsBefore = ss == 1;
26489 var nodeIsAfter = ee == -1;
26491 if (nodeIsBefore && nodeIsAfter) {
26494 if (!nodeIsBefore && nodeIsAfter) {
26495 return 1; //right trailed.
26498 if (nodeIsBefore && !nodeIsAfter) {
26499 return 2; // left trailed.
26505 // private? - in a new class?
26506 cleanUpPaste : function()
26508 // cleans up the whole document..
26509 Roo.log('cleanuppaste');
26511 this.cleanUpChildren(this.doc.body);
26512 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26513 if (clean != this.doc.body.innerHTML) {
26514 this.doc.body.innerHTML = clean;
26519 cleanWordChars : function(input) {// change the chars to hex code
26520 var he = Roo.HtmlEditorCore;
26522 var output = input;
26523 Roo.each(he.swapCodes, function(sw) {
26524 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26526 output = output.replace(swapper, sw[1]);
26533 cleanUpChildren : function (n)
26535 if (!n.childNodes.length) {
26538 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26539 this.cleanUpChild(n.childNodes[i]);
26546 cleanUpChild : function (node)
26549 //console.log(node);
26550 if (node.nodeName == "#text") {
26551 // clean up silly Windows -- stuff?
26554 if (node.nodeName == "#comment") {
26555 if (!this.allowComments) {
26556 node.parentNode.removeChild(node);
26558 // clean up silly Windows -- stuff?
26561 var lcname = node.tagName.toLowerCase();
26562 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26563 // whitelist of tags..
26565 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26567 node.parentNode.removeChild(node);
26572 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26574 // spans with no attributes - just remove them..
26575 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26576 remove_keep_children = true;
26579 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26580 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26582 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26583 // remove_keep_children = true;
26586 if (remove_keep_children) {
26587 this.cleanUpChildren(node);
26588 // inserts everything just before this node...
26589 while (node.childNodes.length) {
26590 var cn = node.childNodes[0];
26591 node.removeChild(cn);
26592 node.parentNode.insertBefore(cn, node);
26594 node.parentNode.removeChild(node);
26598 if (!node.attributes || !node.attributes.length) {
26603 this.cleanUpChildren(node);
26607 function cleanAttr(n,v)
26610 if (v.match(/^\./) || v.match(/^\//)) {
26613 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26616 if (v.match(/^#/)) {
26619 if (v.match(/^\{/)) { // allow template editing.
26622 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26623 node.removeAttribute(n);
26627 var cwhite = this.cwhite;
26628 var cblack = this.cblack;
26630 function cleanStyle(n,v)
26632 if (v.match(/expression/)) { //XSS?? should we even bother..
26633 node.removeAttribute(n);
26637 var parts = v.split(/;/);
26640 Roo.each(parts, function(p) {
26641 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26645 var l = p.split(':').shift().replace(/\s+/g,'');
26646 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26648 if ( cwhite.length && cblack.indexOf(l) > -1) {
26649 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26650 //node.removeAttribute(n);
26654 // only allow 'c whitelisted system attributes'
26655 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26656 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26657 //node.removeAttribute(n);
26667 if (clean.length) {
26668 node.setAttribute(n, clean.join(';'));
26670 node.removeAttribute(n);
26676 for (var i = node.attributes.length-1; i > -1 ; i--) {
26677 var a = node.attributes[i];
26680 if (a.name.toLowerCase().substr(0,2)=='on') {
26681 node.removeAttribute(a.name);
26684 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26685 node.removeAttribute(a.name);
26688 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26689 cleanAttr(a.name,a.value); // fixme..
26692 if (a.name == 'style') {
26693 cleanStyle(a.name,a.value);
26696 /// clean up MS crap..
26697 // tecnically this should be a list of valid class'es..
26700 if (a.name == 'class') {
26701 if (a.value.match(/^Mso/)) {
26702 node.removeAttribute('class');
26705 if (a.value.match(/^body$/)) {
26706 node.removeAttribute('class');
26717 this.cleanUpChildren(node);
26723 * Clean up MS wordisms...
26725 cleanWord : function(node)
26728 this.cleanWord(this.doc.body);
26733 node.nodeName == 'SPAN' &&
26734 !node.hasAttributes() &&
26735 node.childNodes.length == 1 &&
26736 node.firstChild.nodeName == "#text"
26738 var textNode = node.firstChild;
26739 node.removeChild(textNode);
26740 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26741 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26743 node.parentNode.insertBefore(textNode, node);
26744 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26745 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26747 node.parentNode.removeChild(node);
26750 if (node.nodeName == "#text") {
26751 // clean up silly Windows -- stuff?
26754 if (node.nodeName == "#comment") {
26755 node.parentNode.removeChild(node);
26756 // clean up silly Windows -- stuff?
26760 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26761 node.parentNode.removeChild(node);
26764 //Roo.log(node.tagName);
26765 // remove - but keep children..
26766 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26767 //Roo.log('-- removed');
26768 while (node.childNodes.length) {
26769 var cn = node.childNodes[0];
26770 node.removeChild(cn);
26771 node.parentNode.insertBefore(cn, node);
26772 // move node to parent - and clean it..
26773 this.cleanWord(cn);
26775 node.parentNode.removeChild(node);
26776 /// no need to iterate chidlren = it's got none..
26777 //this.iterateChildren(node, this.cleanWord);
26781 if (node.className.length) {
26783 var cn = node.className.split(/\W+/);
26785 Roo.each(cn, function(cls) {
26786 if (cls.match(/Mso[a-zA-Z]+/)) {
26791 node.className = cna.length ? cna.join(' ') : '';
26793 node.removeAttribute("class");
26797 if (node.hasAttribute("lang")) {
26798 node.removeAttribute("lang");
26801 if (node.hasAttribute("style")) {
26803 var styles = node.getAttribute("style").split(";");
26805 Roo.each(styles, function(s) {
26806 if (!s.match(/:/)) {
26809 var kv = s.split(":");
26810 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26813 // what ever is left... we allow.
26816 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26817 if (!nstyle.length) {
26818 node.removeAttribute('style');
26821 this.iterateChildren(node, this.cleanWord);
26827 * iterateChildren of a Node, calling fn each time, using this as the scole..
26828 * @param {DomNode} node node to iterate children of.
26829 * @param {Function} fn method of this class to call on each item.
26831 iterateChildren : function(node, fn)
26833 if (!node.childNodes.length) {
26836 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26837 fn.call(this, node.childNodes[i])
26843 * cleanTableWidths.
26845 * Quite often pasting from word etc.. results in tables with column and widths.
26846 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26849 cleanTableWidths : function(node)
26854 this.cleanTableWidths(this.doc.body);
26859 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26862 Roo.log(node.tagName);
26863 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26864 this.iterateChildren(node, this.cleanTableWidths);
26867 if (node.hasAttribute('width')) {
26868 node.removeAttribute('width');
26872 if (node.hasAttribute("style")) {
26875 var styles = node.getAttribute("style").split(";");
26877 Roo.each(styles, function(s) {
26878 if (!s.match(/:/)) {
26881 var kv = s.split(":");
26882 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26885 // what ever is left... we allow.
26888 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26889 if (!nstyle.length) {
26890 node.removeAttribute('style');
26894 this.iterateChildren(node, this.cleanTableWidths);
26902 domToHTML : function(currentElement, depth, nopadtext) {
26904 depth = depth || 0;
26905 nopadtext = nopadtext || false;
26907 if (!currentElement) {
26908 return this.domToHTML(this.doc.body);
26911 //Roo.log(currentElement);
26913 var allText = false;
26914 var nodeName = currentElement.nodeName;
26915 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26917 if (nodeName == '#text') {
26919 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26924 if (nodeName != 'BODY') {
26927 // Prints the node tagName, such as <A>, <IMG>, etc
26930 for(i = 0; i < currentElement.attributes.length;i++) {
26932 var aname = currentElement.attributes.item(i).name;
26933 if (!currentElement.attributes.item(i).value.length) {
26936 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26939 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26948 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26951 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26956 // Traverse the tree
26958 var currentElementChild = currentElement.childNodes.item(i);
26959 var allText = true;
26960 var innerHTML = '';
26962 while (currentElementChild) {
26963 // Formatting code (indent the tree so it looks nice on the screen)
26964 var nopad = nopadtext;
26965 if (lastnode == 'SPAN') {
26969 if (currentElementChild.nodeName == '#text') {
26970 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26971 toadd = nopadtext ? toadd : toadd.trim();
26972 if (!nopad && toadd.length > 80) {
26973 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26975 innerHTML += toadd;
26978 currentElementChild = currentElement.childNodes.item(i);
26984 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26986 // Recursively traverse the tree structure of the child node
26987 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26988 lastnode = currentElementChild.nodeName;
26990 currentElementChild=currentElement.childNodes.item(i);
26996 // The remaining code is mostly for formatting the tree
26997 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
27002 ret+= "</"+tagName+">";
27008 applyBlacklists : function()
27010 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
27011 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
27015 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27016 if (b.indexOf(tag) > -1) {
27019 this.white.push(tag);
27023 Roo.each(w, function(tag) {
27024 if (b.indexOf(tag) > -1) {
27027 if (this.white.indexOf(tag) > -1) {
27030 this.white.push(tag);
27035 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27036 if (w.indexOf(tag) > -1) {
27039 this.black.push(tag);
27043 Roo.each(b, function(tag) {
27044 if (w.indexOf(tag) > -1) {
27047 if (this.black.indexOf(tag) > -1) {
27050 this.black.push(tag);
27055 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27056 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27060 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27061 if (b.indexOf(tag) > -1) {
27064 this.cwhite.push(tag);
27068 Roo.each(w, function(tag) {
27069 if (b.indexOf(tag) > -1) {
27072 if (this.cwhite.indexOf(tag) > -1) {
27075 this.cwhite.push(tag);
27080 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27081 if (w.indexOf(tag) > -1) {
27084 this.cblack.push(tag);
27088 Roo.each(b, function(tag) {
27089 if (w.indexOf(tag) > -1) {
27092 if (this.cblack.indexOf(tag) > -1) {
27095 this.cblack.push(tag);
27100 setStylesheets : function(stylesheets)
27102 if(typeof(stylesheets) == 'string'){
27103 Roo.get(this.iframe.contentDocument.head).createChild({
27105 rel : 'stylesheet',
27114 Roo.each(stylesheets, function(s) {
27119 Roo.get(_this.iframe.contentDocument.head).createChild({
27121 rel : 'stylesheet',
27130 removeStylesheets : function()
27134 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27139 setStyle : function(style)
27141 Roo.get(this.iframe.contentDocument.head).createChild({
27150 // hide stuff that is not compatible
27164 * @event specialkey
27168 * @cfg {String} fieldClass @hide
27171 * @cfg {String} focusClass @hide
27174 * @cfg {String} autoCreate @hide
27177 * @cfg {String} inputType @hide
27180 * @cfg {String} invalidClass @hide
27183 * @cfg {String} invalidText @hide
27186 * @cfg {String} msgFx @hide
27189 * @cfg {String} validateOnBlur @hide
27193 Roo.HtmlEditorCore.white = [
27194 'area', 'br', 'img', 'input', 'hr', 'wbr',
27196 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27197 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27198 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27199 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27200 'table', 'ul', 'xmp',
27202 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27205 'dir', 'menu', 'ol', 'ul', 'dl',
27211 Roo.HtmlEditorCore.black = [
27212 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27214 'base', 'basefont', 'bgsound', 'blink', 'body',
27215 'frame', 'frameset', 'head', 'html', 'ilayer',
27216 'iframe', 'layer', 'link', 'meta', 'object',
27217 'script', 'style' ,'title', 'xml' // clean later..
27219 Roo.HtmlEditorCore.clean = [
27220 'script', 'style', 'title', 'xml'
27222 Roo.HtmlEditorCore.remove = [
27227 Roo.HtmlEditorCore.ablack = [
27231 Roo.HtmlEditorCore.aclean = [
27232 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27236 Roo.HtmlEditorCore.pwhite= [
27237 'http', 'https', 'mailto'
27240 // white listed style attributes.
27241 Roo.HtmlEditorCore.cwhite= [
27242 // 'text-align', /// default is to allow most things..
27248 // black listed style attributes.
27249 Roo.HtmlEditorCore.cblack= [
27250 // 'font-size' -- this can be set by the project
27254 Roo.HtmlEditorCore.swapCodes =[
27255 [ 8211, "–" ],
27256 [ 8212, "—" ],
27273 * @class Roo.bootstrap.HtmlEditor
27274 * @extends Roo.bootstrap.TextArea
27275 * Bootstrap HtmlEditor class
27278 * Create a new HtmlEditor
27279 * @param {Object} config The config object
27282 Roo.bootstrap.HtmlEditor = function(config){
27283 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27284 if (!this.toolbars) {
27285 this.toolbars = [];
27288 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27291 * @event initialize
27292 * Fires when the editor is fully initialized (including the iframe)
27293 * @param {HtmlEditor} this
27298 * Fires when the editor is first receives the focus. Any insertion must wait
27299 * until after this event.
27300 * @param {HtmlEditor} this
27304 * @event beforesync
27305 * Fires before the textarea is updated with content from the editor iframe. Return false
27306 * to cancel the sync.
27307 * @param {HtmlEditor} this
27308 * @param {String} html
27312 * @event beforepush
27313 * Fires before the iframe editor is updated with content from the textarea. Return false
27314 * to cancel the push.
27315 * @param {HtmlEditor} this
27316 * @param {String} html
27321 * Fires when the textarea is updated with content from the editor iframe.
27322 * @param {HtmlEditor} this
27323 * @param {String} html
27328 * Fires when the iframe editor is updated with content from the textarea.
27329 * @param {HtmlEditor} this
27330 * @param {String} html
27334 * @event editmodechange
27335 * Fires when the editor switches edit modes
27336 * @param {HtmlEditor} this
27337 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27339 editmodechange: true,
27341 * @event editorevent
27342 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27343 * @param {HtmlEditor} this
27347 * @event firstfocus
27348 * Fires when on first focus - needed by toolbars..
27349 * @param {HtmlEditor} this
27354 * Auto save the htmlEditor value as a file into Events
27355 * @param {HtmlEditor} this
27359 * @event savedpreview
27360 * preview the saved version of htmlEditor
27361 * @param {HtmlEditor} this
27368 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27372 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27377 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27382 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27387 * @cfg {Number} height (in pixels)
27391 * @cfg {Number} width (in pixels)
27396 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27399 stylesheets: false,
27404 // private properties
27405 validationEvent : false,
27407 initialized : false,
27410 onFocus : Roo.emptyFn,
27412 hideMode:'offsets',
27414 tbContainer : false,
27418 toolbarContainer :function() {
27419 return this.wrap.select('.x-html-editor-tb',true).first();
27423 * Protected method that will not generally be called directly. It
27424 * is called when the editor creates its toolbar. Override this method if you need to
27425 * add custom toolbar buttons.
27426 * @param {HtmlEditor} editor
27428 createToolbar : function(){
27429 Roo.log('renewing');
27430 Roo.log("create toolbars");
27432 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27433 this.toolbars[0].render(this.toolbarContainer());
27437 // if (!editor.toolbars || !editor.toolbars.length) {
27438 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27441 // for (var i =0 ; i < editor.toolbars.length;i++) {
27442 // editor.toolbars[i] = Roo.factory(
27443 // typeof(editor.toolbars[i]) == 'string' ?
27444 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27445 // Roo.bootstrap.HtmlEditor);
27446 // editor.toolbars[i].init(editor);
27452 onRender : function(ct, position)
27454 // Roo.log("Call onRender: " + this.xtype);
27456 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27458 this.wrap = this.inputEl().wrap({
27459 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27462 this.editorcore.onRender(ct, position);
27464 if (this.resizable) {
27465 this.resizeEl = new Roo.Resizable(this.wrap, {
27469 minHeight : this.height,
27470 height: this.height,
27471 handles : this.resizable,
27474 resize : function(r, w, h) {
27475 _t.onResize(w,h); // -something
27481 this.createToolbar(this);
27484 if(!this.width && this.resizable){
27485 this.setSize(this.wrap.getSize());
27487 if (this.resizeEl) {
27488 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27489 // should trigger onReize..
27495 onResize : function(w, h)
27497 Roo.log('resize: ' +w + ',' + h );
27498 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27502 if(this.inputEl() ){
27503 if(typeof w == 'number'){
27504 var aw = w - this.wrap.getFrameWidth('lr');
27505 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27508 if(typeof h == 'number'){
27509 var tbh = -11; // fixme it needs to tool bar size!
27510 for (var i =0; i < this.toolbars.length;i++) {
27511 // fixme - ask toolbars for heights?
27512 tbh += this.toolbars[i].el.getHeight();
27513 //if (this.toolbars[i].footer) {
27514 // tbh += this.toolbars[i].footer.el.getHeight();
27522 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27523 ah -= 5; // knock a few pixes off for look..
27524 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27528 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27529 this.editorcore.onResize(ew,eh);
27534 * Toggles the editor between standard and source edit mode.
27535 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27537 toggleSourceEdit : function(sourceEditMode)
27539 this.editorcore.toggleSourceEdit(sourceEditMode);
27541 if(this.editorcore.sourceEditMode){
27542 Roo.log('editor - showing textarea');
27545 // Roo.log(this.syncValue());
27547 this.inputEl().removeClass(['hide', 'x-hidden']);
27548 this.inputEl().dom.removeAttribute('tabIndex');
27549 this.inputEl().focus();
27551 Roo.log('editor - hiding textarea');
27553 // Roo.log(this.pushValue());
27556 this.inputEl().addClass(['hide', 'x-hidden']);
27557 this.inputEl().dom.setAttribute('tabIndex', -1);
27558 //this.deferFocus();
27561 if(this.resizable){
27562 this.setSize(this.wrap.getSize());
27565 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27568 // private (for BoxComponent)
27569 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27571 // private (for BoxComponent)
27572 getResizeEl : function(){
27576 // private (for BoxComponent)
27577 getPositionEl : function(){
27582 initEvents : function(){
27583 this.originalValue = this.getValue();
27587 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27590 // markInvalid : Roo.emptyFn,
27592 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27595 // clearInvalid : Roo.emptyFn,
27597 setValue : function(v){
27598 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27599 this.editorcore.pushValue();
27604 deferFocus : function(){
27605 this.focus.defer(10, this);
27609 focus : function(){
27610 this.editorcore.focus();
27616 onDestroy : function(){
27622 for (var i =0; i < this.toolbars.length;i++) {
27623 // fixme - ask toolbars for heights?
27624 this.toolbars[i].onDestroy();
27627 this.wrap.dom.innerHTML = '';
27628 this.wrap.remove();
27633 onFirstFocus : function(){
27634 //Roo.log("onFirstFocus");
27635 this.editorcore.onFirstFocus();
27636 for (var i =0; i < this.toolbars.length;i++) {
27637 this.toolbars[i].onFirstFocus();
27643 syncValue : function()
27645 this.editorcore.syncValue();
27648 pushValue : function()
27650 this.editorcore.pushValue();
27654 // hide stuff that is not compatible
27668 * @event specialkey
27672 * @cfg {String} fieldClass @hide
27675 * @cfg {String} focusClass @hide
27678 * @cfg {String} autoCreate @hide
27681 * @cfg {String} inputType @hide
27685 * @cfg {String} invalidText @hide
27688 * @cfg {String} msgFx @hide
27691 * @cfg {String} validateOnBlur @hide
27700 Roo.namespace('Roo.bootstrap.htmleditor');
27702 * @class Roo.bootstrap.HtmlEditorToolbar1
27708 new Roo.bootstrap.HtmlEditor({
27711 new Roo.bootstrap.HtmlEditorToolbar1({
27712 disable : { fonts: 1 , format: 1, ..., ... , ...],
27718 * @cfg {Object} disable List of elements to disable..
27719 * @cfg {Array} btns List of additional buttons.
27723 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27726 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27729 Roo.apply(this, config);
27731 // default disabled, based on 'good practice'..
27732 this.disable = this.disable || {};
27733 Roo.applyIf(this.disable, {
27736 specialElements : true
27738 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27740 this.editor = config.editor;
27741 this.editorcore = config.editor.editorcore;
27743 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27745 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27746 // dont call parent... till later.
27748 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27753 editorcore : false,
27758 "h1","h2","h3","h4","h5","h6",
27760 "abbr", "acronym", "address", "cite", "samp", "var",
27764 onRender : function(ct, position)
27766 // Roo.log("Call onRender: " + this.xtype);
27768 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27770 this.el.dom.style.marginBottom = '0';
27772 var editorcore = this.editorcore;
27773 var editor= this.editor;
27776 var btn = function(id,cmd , toggle, handler, html){
27778 var event = toggle ? 'toggle' : 'click';
27783 xns: Roo.bootstrap,
27787 enableToggle:toggle !== false,
27789 pressed : toggle ? false : null,
27792 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27793 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27799 // var cb_box = function...
27804 xns: Roo.bootstrap,
27809 xns: Roo.bootstrap,
27813 Roo.each(this.formats, function(f) {
27814 style.menu.items.push({
27816 xns: Roo.bootstrap,
27817 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27822 editorcore.insertTag(this.tagname);
27829 children.push(style);
27831 btn('bold',false,true);
27832 btn('italic',false,true);
27833 btn('align-left', 'justifyleft',true);
27834 btn('align-center', 'justifycenter',true);
27835 btn('align-right' , 'justifyright',true);
27836 btn('link', false, false, function(btn) {
27837 //Roo.log("create link?");
27838 var url = prompt(this.createLinkText, this.defaultLinkValue);
27839 if(url && url != 'http:/'+'/'){
27840 this.editorcore.relayCmd('createlink', url);
27843 btn('list','insertunorderedlist',true);
27844 btn('pencil', false,true, function(btn){
27846 this.toggleSourceEdit(btn.pressed);
27849 if (this.editor.btns.length > 0) {
27850 for (var i = 0; i<this.editor.btns.length; i++) {
27851 children.push(this.editor.btns[i]);
27859 xns: Roo.bootstrap,
27864 xns: Roo.bootstrap,
27869 cog.menu.items.push({
27871 xns: Roo.bootstrap,
27872 html : Clean styles,
27877 editorcore.insertTag(this.tagname);
27886 this.xtype = 'NavSimplebar';
27888 for(var i=0;i< children.length;i++) {
27890 this.buttons.add(this.addxtypeChild(children[i]));
27894 editor.on('editorevent', this.updateToolbar, this);
27896 onBtnClick : function(id)
27898 this.editorcore.relayCmd(id);
27899 this.editorcore.focus();
27903 * Protected method that will not generally be called directly. It triggers
27904 * a toolbar update by reading the markup state of the current selection in the editor.
27906 updateToolbar: function(){
27908 if(!this.editorcore.activated){
27909 this.editor.onFirstFocus(); // is this neeed?
27913 var btns = this.buttons;
27914 var doc = this.editorcore.doc;
27915 btns.get('bold').setActive(doc.queryCommandState('bold'));
27916 btns.get('italic').setActive(doc.queryCommandState('italic'));
27917 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27919 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27920 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27921 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27923 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27924 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27927 var ans = this.editorcore.getAllAncestors();
27928 if (this.formatCombo) {
27931 var store = this.formatCombo.store;
27932 this.formatCombo.setValue("");
27933 for (var i =0; i < ans.length;i++) {
27934 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27936 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27944 // hides menus... - so this cant be on a menu...
27945 Roo.bootstrap.MenuMgr.hideAll();
27947 Roo.bootstrap.MenuMgr.hideAll();
27948 //this.editorsyncValue();
27950 onFirstFocus: function() {
27951 this.buttons.each(function(item){
27955 toggleSourceEdit : function(sourceEditMode){
27958 if(sourceEditMode){
27959 Roo.log("disabling buttons");
27960 this.buttons.each( function(item){
27961 if(item.cmd != 'pencil'){
27967 Roo.log("enabling buttons");
27968 if(this.editorcore.initialized){
27969 this.buttons.each( function(item){
27975 Roo.log("calling toggole on editor");
27976 // tell the editor that it's been pressed..
27977 this.editor.toggleSourceEdit(sourceEditMode);
27991 * @class Roo.bootstrap.Markdown
27992 * @extends Roo.bootstrap.TextArea
27993 * Bootstrap Showdown editable area
27994 * @cfg {string} content
27997 * Create a new Showdown
28000 Roo.bootstrap.Markdown = function(config){
28001 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
28005 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
28009 initEvents : function()
28012 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
28013 this.markdownEl = this.el.createChild({
28014 cls : 'roo-markdown-area'
28016 this.inputEl().addClass('d-none');
28017 if (this.getValue() == '') {
28018 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28021 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28023 this.markdownEl.on('click', this.toggleTextEdit, this);
28024 this.on('blur', this.toggleTextEdit, this);
28025 this.on('specialkey', this.resizeTextArea, this);
28028 toggleTextEdit : function()
28030 var sh = this.markdownEl.getHeight();
28031 this.inputEl().addClass('d-none');
28032 this.markdownEl.addClass('d-none');
28033 if (!this.editing) {
28035 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28036 this.inputEl().removeClass('d-none');
28037 this.inputEl().focus();
28038 this.editing = true;
28041 // show showdown...
28042 this.updateMarkdown();
28043 this.markdownEl.removeClass('d-none');
28044 this.editing = false;
28047 updateMarkdown : function()
28049 if (this.getValue() == '') {
28050 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28054 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28057 resizeTextArea: function () {
28060 Roo.log([sh, this.getValue().split("\n").length * 30]);
28061 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28063 setValue : function(val)
28065 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28066 if (!this.editing) {
28067 this.updateMarkdown();
28073 if (!this.editing) {
28074 this.toggleTextEdit();
28082 * Ext JS Library 1.1.1
28083 * Copyright(c) 2006-2007, Ext JS, LLC.
28085 * Originally Released Under LGPL - original licence link has changed is not relivant.
28088 * <script type="text/javascript">
28092 * @class Roo.bootstrap.PagingToolbar
28093 * @extends Roo.bootstrap.NavSimplebar
28094 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28096 * Create a new PagingToolbar
28097 * @param {Object} config The config object
28098 * @param {Roo.data.Store} store
28100 Roo.bootstrap.PagingToolbar = function(config)
28102 // old args format still supported... - xtype is prefered..
28103 // created from xtype...
28105 this.ds = config.dataSource;
28107 if (config.store && !this.ds) {
28108 this.store= Roo.factory(config.store, Roo.data);
28109 this.ds = this.store;
28110 this.ds.xmodule = this.xmodule || false;
28113 this.toolbarItems = [];
28114 if (config.items) {
28115 this.toolbarItems = config.items;
28118 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28123 this.bind(this.ds);
28126 if (Roo.bootstrap.version == 4) {
28127 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28129 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28134 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28136 * @cfg {Roo.data.Store} dataSource
28137 * The underlying data store providing the paged data
28140 * @cfg {String/HTMLElement/Element} container
28141 * container The id or element that will contain the toolbar
28144 * @cfg {Boolean} displayInfo
28145 * True to display the displayMsg (defaults to false)
28148 * @cfg {Number} pageSize
28149 * The number of records to display per page (defaults to 20)
28153 * @cfg {String} displayMsg
28154 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28156 displayMsg : 'Displaying {0} - {1} of {2}',
28158 * @cfg {String} emptyMsg
28159 * The message to display when no records are found (defaults to "No data to display")
28161 emptyMsg : 'No data to display',
28163 * Customizable piece of the default paging text (defaults to "Page")
28166 beforePageText : "Page",
28168 * Customizable piece of the default paging text (defaults to "of %0")
28171 afterPageText : "of {0}",
28173 * Customizable piece of the default paging text (defaults to "First Page")
28176 firstText : "First Page",
28178 * Customizable piece of the default paging text (defaults to "Previous Page")
28181 prevText : "Previous Page",
28183 * Customizable piece of the default paging text (defaults to "Next Page")
28186 nextText : "Next Page",
28188 * Customizable piece of the default paging text (defaults to "Last Page")
28191 lastText : "Last Page",
28193 * Customizable piece of the default paging text (defaults to "Refresh")
28196 refreshText : "Refresh",
28200 onRender : function(ct, position)
28202 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28203 this.navgroup.parentId = this.id;
28204 this.navgroup.onRender(this.el, null);
28205 // add the buttons to the navgroup
28207 if(this.displayInfo){
28208 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28209 this.displayEl = this.el.select('.x-paging-info', true).first();
28210 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28211 // this.displayEl = navel.el.select('span',true).first();
28217 Roo.each(_this.buttons, function(e){ // this might need to use render????
28218 Roo.factory(e).render(_this.el);
28222 Roo.each(_this.toolbarItems, function(e) {
28223 _this.navgroup.addItem(e);
28227 this.first = this.navgroup.addItem({
28228 tooltip: this.firstText,
28229 cls: "prev btn-outline-secondary",
28230 html : ' <i class="fa fa-step-backward"></i>',
28232 preventDefault: true,
28233 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28236 this.prev = this.navgroup.addItem({
28237 tooltip: this.prevText,
28238 cls: "prev btn-outline-secondary",
28239 html : ' <i class="fa fa-backward"></i>',
28241 preventDefault: true,
28242 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28244 //this.addSeparator();
28247 var field = this.navgroup.addItem( {
28249 cls : 'x-paging-position btn-outline-secondary',
28251 html : this.beforePageText +
28252 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28253 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28256 this.field = field.el.select('input', true).first();
28257 this.field.on("keydown", this.onPagingKeydown, this);
28258 this.field.on("focus", function(){this.dom.select();});
28261 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28262 //this.field.setHeight(18);
28263 //this.addSeparator();
28264 this.next = this.navgroup.addItem({
28265 tooltip: this.nextText,
28266 cls: "next btn-outline-secondary",
28267 html : ' <i class="fa fa-forward"></i>',
28269 preventDefault: true,
28270 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28272 this.last = this.navgroup.addItem({
28273 tooltip: this.lastText,
28274 html : ' <i class="fa fa-step-forward"></i>',
28275 cls: "next btn-outline-secondary",
28277 preventDefault: true,
28278 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28280 //this.addSeparator();
28281 this.loading = this.navgroup.addItem({
28282 tooltip: this.refreshText,
28283 cls: "btn-outline-secondary",
28284 html : ' <i class="fa fa-refresh"></i>',
28285 preventDefault: true,
28286 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28292 updateInfo : function(){
28293 if(this.displayEl){
28294 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28295 var msg = count == 0 ?
28299 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28301 this.displayEl.update(msg);
28306 onLoad : function(ds, r, o)
28308 this.cursor = o.params && o.params.start ? o.params.start : 0;
28310 var d = this.getPageData(),
28315 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28316 this.field.dom.value = ap;
28317 this.first.setDisabled(ap == 1);
28318 this.prev.setDisabled(ap == 1);
28319 this.next.setDisabled(ap == ps);
28320 this.last.setDisabled(ap == ps);
28321 this.loading.enable();
28326 getPageData : function(){
28327 var total = this.ds.getTotalCount();
28330 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28331 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28336 onLoadError : function(){
28337 this.loading.enable();
28341 onPagingKeydown : function(e){
28342 var k = e.getKey();
28343 var d = this.getPageData();
28345 var v = this.field.dom.value, pageNum;
28346 if(!v || isNaN(pageNum = parseInt(v, 10))){
28347 this.field.dom.value = d.activePage;
28350 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28351 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28354 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))
28356 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28357 this.field.dom.value = pageNum;
28358 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28361 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28363 var v = this.field.dom.value, pageNum;
28364 var increment = (e.shiftKey) ? 10 : 1;
28365 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28368 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28369 this.field.dom.value = d.activePage;
28372 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28374 this.field.dom.value = parseInt(v, 10) + increment;
28375 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28376 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28383 beforeLoad : function(){
28385 this.loading.disable();
28390 onClick : function(which){
28399 ds.load({params:{start: 0, limit: this.pageSize}});
28402 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28405 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28408 var total = ds.getTotalCount();
28409 var extra = total % this.pageSize;
28410 var lastStart = extra ? (total - extra) : total-this.pageSize;
28411 ds.load({params:{start: lastStart, limit: this.pageSize}});
28414 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28420 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28421 * @param {Roo.data.Store} store The data store to unbind
28423 unbind : function(ds){
28424 ds.un("beforeload", this.beforeLoad, this);
28425 ds.un("load", this.onLoad, this);
28426 ds.un("loadexception", this.onLoadError, this);
28427 ds.un("remove", this.updateInfo, this);
28428 ds.un("add", this.updateInfo, this);
28429 this.ds = undefined;
28433 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28434 * @param {Roo.data.Store} store The data store to bind
28436 bind : function(ds){
28437 ds.on("beforeload", this.beforeLoad, this);
28438 ds.on("load", this.onLoad, this);
28439 ds.on("loadexception", this.onLoadError, this);
28440 ds.on("remove", this.updateInfo, this);
28441 ds.on("add", this.updateInfo, this);
28452 * @class Roo.bootstrap.MessageBar
28453 * @extends Roo.bootstrap.Component
28454 * Bootstrap MessageBar class
28455 * @cfg {String} html contents of the MessageBar
28456 * @cfg {String} weight (info | success | warning | danger) default info
28457 * @cfg {String} beforeClass insert the bar before the given class
28458 * @cfg {Boolean} closable (true | false) default false
28459 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28462 * Create a new Element
28463 * @param {Object} config The config object
28466 Roo.bootstrap.MessageBar = function(config){
28467 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28470 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28476 beforeClass: 'bootstrap-sticky-wrap',
28478 getAutoCreate : function(){
28482 cls: 'alert alert-dismissable alert-' + this.weight,
28487 html: this.html || ''
28493 cfg.cls += ' alert-messages-fixed';
28507 onRender : function(ct, position)
28509 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28512 var cfg = Roo.apply({}, this.getAutoCreate());
28516 cfg.cls += ' ' + this.cls;
28519 cfg.style = this.style;
28521 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28523 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28526 this.el.select('>button.close').on('click', this.hide, this);
28532 if (!this.rendered) {
28538 this.fireEvent('show', this);
28544 if (!this.rendered) {
28550 this.fireEvent('hide', this);
28553 update : function()
28555 // var e = this.el.dom.firstChild;
28557 // if(this.closable){
28558 // e = e.nextSibling;
28561 // e.data = this.html || '';
28563 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28579 * @class Roo.bootstrap.Graph
28580 * @extends Roo.bootstrap.Component
28581 * Bootstrap Graph class
28585 @cfg {String} graphtype bar | vbar | pie
28586 @cfg {number} g_x coodinator | centre x (pie)
28587 @cfg {number} g_y coodinator | centre y (pie)
28588 @cfg {number} g_r radius (pie)
28589 @cfg {number} g_height height of the chart (respected by all elements in the set)
28590 @cfg {number} g_width width of the chart (respected by all elements in the set)
28591 @cfg {Object} title The title of the chart
28594 -opts (object) options for the chart
28596 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28597 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28599 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.
28600 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28602 o stretch (boolean)
28604 -opts (object) options for the pie
28607 o startAngle (number)
28608 o endAngle (number)
28612 * Create a new Input
28613 * @param {Object} config The config object
28616 Roo.bootstrap.Graph = function(config){
28617 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28623 * The img click event for the img.
28624 * @param {Roo.EventObject} e
28630 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28641 //g_colors: this.colors,
28648 getAutoCreate : function(){
28659 onRender : function(ct,position){
28662 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28664 if (typeof(Raphael) == 'undefined') {
28665 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28669 this.raphael = Raphael(this.el.dom);
28671 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28672 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28673 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28674 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28676 r.text(160, 10, "Single Series Chart").attr(txtattr);
28677 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28678 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28679 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28681 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28682 r.barchart(330, 10, 300, 220, data1);
28683 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28684 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28687 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28688 // r.barchart(30, 30, 560, 250, xdata, {
28689 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28690 // axis : "0 0 1 1",
28691 // axisxlabels : xdata
28692 // //yvalues : cols,
28695 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28697 // this.load(null,xdata,{
28698 // axis : "0 0 1 1",
28699 // axisxlabels : xdata
28704 load : function(graphtype,xdata,opts)
28706 this.raphael.clear();
28708 graphtype = this.graphtype;
28713 var r = this.raphael,
28714 fin = function () {
28715 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28717 fout = function () {
28718 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28720 pfin = function() {
28721 this.sector.stop();
28722 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28725 this.label[0].stop();
28726 this.label[0].attr({ r: 7.5 });
28727 this.label[1].attr({ "font-weight": 800 });
28730 pfout = function() {
28731 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28734 this.label[0].animate({ r: 5 }, 500, "bounce");
28735 this.label[1].attr({ "font-weight": 400 });
28741 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28744 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28747 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28748 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28750 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28757 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28762 setTitle: function(o)
28767 initEvents: function() {
28770 this.el.on('click', this.onClick, this);
28774 onClick : function(e)
28776 Roo.log('img onclick');
28777 this.fireEvent('click', this, e);
28789 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28792 * @class Roo.bootstrap.dash.NumberBox
28793 * @extends Roo.bootstrap.Component
28794 * Bootstrap NumberBox class
28795 * @cfg {String} headline Box headline
28796 * @cfg {String} content Box content
28797 * @cfg {String} icon Box icon
28798 * @cfg {String} footer Footer text
28799 * @cfg {String} fhref Footer href
28802 * Create a new NumberBox
28803 * @param {Object} config The config object
28807 Roo.bootstrap.dash.NumberBox = function(config){
28808 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28812 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28821 getAutoCreate : function(){
28825 cls : 'small-box ',
28833 cls : 'roo-headline',
28834 html : this.headline
28838 cls : 'roo-content',
28839 html : this.content
28853 cls : 'ion ' + this.icon
28862 cls : 'small-box-footer',
28863 href : this.fhref || '#',
28867 cfg.cn.push(footer);
28874 onRender : function(ct,position){
28875 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28882 setHeadline: function (value)
28884 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28887 setFooter: function (value, href)
28889 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28892 this.el.select('a.small-box-footer',true).first().attr('href', href);
28897 setContent: function (value)
28899 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28902 initEvents: function()
28916 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28919 * @class Roo.bootstrap.dash.TabBox
28920 * @extends Roo.bootstrap.Component
28921 * Bootstrap TabBox class
28922 * @cfg {String} title Title of the TabBox
28923 * @cfg {String} icon Icon of the TabBox
28924 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28925 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28928 * Create a new TabBox
28929 * @param {Object} config The config object
28933 Roo.bootstrap.dash.TabBox = function(config){
28934 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28939 * When a pane is added
28940 * @param {Roo.bootstrap.dash.TabPane} pane
28944 * @event activatepane
28945 * When a pane is activated
28946 * @param {Roo.bootstrap.dash.TabPane} pane
28948 "activatepane" : true
28956 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28961 tabScrollable : false,
28963 getChildContainer : function()
28965 return this.el.select('.tab-content', true).first();
28968 getAutoCreate : function(){
28972 cls: 'pull-left header',
28980 cls: 'fa ' + this.icon
28986 cls: 'nav nav-tabs pull-right',
28992 if(this.tabScrollable){
28999 cls: 'nav nav-tabs pull-right',
29010 cls: 'nav-tabs-custom',
29015 cls: 'tab-content no-padding',
29023 initEvents : function()
29025 //Roo.log('add add pane handler');
29026 this.on('addpane', this.onAddPane, this);
29029 * Updates the box title
29030 * @param {String} html to set the title to.
29032 setTitle : function(value)
29034 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29036 onAddPane : function(pane)
29038 this.panes.push(pane);
29039 //Roo.log('addpane');
29041 // tabs are rendere left to right..
29042 if(!this.showtabs){
29046 var ctr = this.el.select('.nav-tabs', true).first();
29049 var existing = ctr.select('.nav-tab',true);
29050 var qty = existing.getCount();;
29053 var tab = ctr.createChild({
29055 cls : 'nav-tab' + (qty ? '' : ' active'),
29063 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29066 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29068 pane.el.addClass('active');
29073 onTabClick : function(ev,un,ob,pane)
29075 //Roo.log('tab - prev default');
29076 ev.preventDefault();
29079 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29080 pane.tab.addClass('active');
29081 //Roo.log(pane.title);
29082 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29083 // technically we should have a deactivate event.. but maybe add later.
29084 // and it should not de-activate the selected tab...
29085 this.fireEvent('activatepane', pane);
29086 pane.el.addClass('active');
29087 pane.fireEvent('activate');
29092 getActivePane : function()
29095 Roo.each(this.panes, function(p) {
29096 if(p.el.hasClass('active')){
29117 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29119 * @class Roo.bootstrap.TabPane
29120 * @extends Roo.bootstrap.Component
29121 * Bootstrap TabPane class
29122 * @cfg {Boolean} active (false | true) Default false
29123 * @cfg {String} title title of panel
29127 * Create a new TabPane
29128 * @param {Object} config The config object
29131 Roo.bootstrap.dash.TabPane = function(config){
29132 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29138 * When a pane is activated
29139 * @param {Roo.bootstrap.dash.TabPane} pane
29146 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29151 // the tabBox that this is attached to.
29154 getAutoCreate : function()
29162 cfg.cls += ' active';
29167 initEvents : function()
29169 //Roo.log('trigger add pane handler');
29170 this.parent().fireEvent('addpane', this)
29174 * Updates the tab title
29175 * @param {String} html to set the title to.
29177 setTitle: function(str)
29183 this.tab.select('a', true).first().dom.innerHTML = str;
29200 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29203 * @class Roo.bootstrap.menu.Menu
29204 * @extends Roo.bootstrap.Component
29205 * Bootstrap Menu class - container for Menu
29206 * @cfg {String} html Text of the menu
29207 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29208 * @cfg {String} icon Font awesome icon
29209 * @cfg {String} pos Menu align to (top | bottom) default bottom
29213 * Create a new Menu
29214 * @param {Object} config The config object
29218 Roo.bootstrap.menu.Menu = function(config){
29219 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29223 * @event beforeshow
29224 * Fires before this menu is displayed
29225 * @param {Roo.bootstrap.menu.Menu} this
29229 * @event beforehide
29230 * Fires before this menu is hidden
29231 * @param {Roo.bootstrap.menu.Menu} this
29236 * Fires after this menu is displayed
29237 * @param {Roo.bootstrap.menu.Menu} this
29242 * Fires after this menu is hidden
29243 * @param {Roo.bootstrap.menu.Menu} this
29248 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29249 * @param {Roo.bootstrap.menu.Menu} this
29250 * @param {Roo.EventObject} e
29257 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
29261 weight : 'default',
29266 getChildContainer : function() {
29267 if(this.isSubMenu){
29271 return this.el.select('ul.dropdown-menu', true).first();
29274 getAutoCreate : function()
29279 cls : 'roo-menu-text',
29287 cls : 'fa ' + this.icon
29298 cls : 'dropdown-button btn btn-' + this.weight,
29303 cls : 'dropdown-toggle btn btn-' + this.weight,
29313 cls : 'dropdown-menu'
29319 if(this.pos == 'top'){
29320 cfg.cls += ' dropup';
29323 if(this.isSubMenu){
29326 cls : 'dropdown-menu'
29333 onRender : function(ct, position)
29335 this.isSubMenu = ct.hasClass('dropdown-submenu');
29337 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29340 initEvents : function()
29342 if(this.isSubMenu){
29346 this.hidden = true;
29348 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29349 this.triggerEl.on('click', this.onTriggerPress, this);
29351 this.buttonEl = this.el.select('button.dropdown-button', true).first();
29352 this.buttonEl.on('click', this.onClick, this);
29358 if(this.isSubMenu){
29362 return this.el.select('ul.dropdown-menu', true).first();
29365 onClick : function(e)
29367 this.fireEvent("click", this, e);
29370 onTriggerPress : function(e)
29372 if (this.isVisible()) {
29379 isVisible : function(){
29380 return !this.hidden;
29385 this.fireEvent("beforeshow", this);
29387 this.hidden = false;
29388 this.el.addClass('open');
29390 Roo.get(document).on("mouseup", this.onMouseUp, this);
29392 this.fireEvent("show", this);
29399 this.fireEvent("beforehide", this);
29401 this.hidden = true;
29402 this.el.removeClass('open');
29404 Roo.get(document).un("mouseup", this.onMouseUp);
29406 this.fireEvent("hide", this);
29409 onMouseUp : function()
29423 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29426 * @class Roo.bootstrap.menu.Item
29427 * @extends Roo.bootstrap.Component
29428 * Bootstrap MenuItem class
29429 * @cfg {Boolean} submenu (true | false) default false
29430 * @cfg {String} html text of the item
29431 * @cfg {String} href the link
29432 * @cfg {Boolean} disable (true | false) default false
29433 * @cfg {Boolean} preventDefault (true | false) default true
29434 * @cfg {String} icon Font awesome icon
29435 * @cfg {String} pos Submenu align to (left | right) default right
29439 * Create a new Item
29440 * @param {Object} config The config object
29444 Roo.bootstrap.menu.Item = function(config){
29445 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29449 * Fires when the mouse is hovering over this menu
29450 * @param {Roo.bootstrap.menu.Item} this
29451 * @param {Roo.EventObject} e
29456 * Fires when the mouse exits this menu
29457 * @param {Roo.bootstrap.menu.Item} this
29458 * @param {Roo.EventObject} e
29464 * The raw click event for the entire grid.
29465 * @param {Roo.EventObject} e
29471 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
29476 preventDefault: true,
29481 getAutoCreate : function()
29486 cls : 'roo-menu-item-text',
29494 cls : 'fa ' + this.icon
29503 href : this.href || '#',
29510 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29514 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29516 if(this.pos == 'left'){
29517 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29524 initEvents : function()
29526 this.el.on('mouseover', this.onMouseOver, this);
29527 this.el.on('mouseout', this.onMouseOut, this);
29529 this.el.select('a', true).first().on('click', this.onClick, this);
29533 onClick : function(e)
29535 if(this.preventDefault){
29536 e.preventDefault();
29539 this.fireEvent("click", this, e);
29542 onMouseOver : function(e)
29544 if(this.submenu && this.pos == 'left'){
29545 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29548 this.fireEvent("mouseover", this, e);
29551 onMouseOut : function(e)
29553 this.fireEvent("mouseout", this, e);
29565 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29568 * @class Roo.bootstrap.menu.Separator
29569 * @extends Roo.bootstrap.Component
29570 * Bootstrap Separator class
29573 * Create a new Separator
29574 * @param {Object} config The config object
29578 Roo.bootstrap.menu.Separator = function(config){
29579 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29582 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29584 getAutoCreate : function(){
29587 cls: 'dropdown-divider divider'
29605 * @class Roo.bootstrap.Tooltip
29606 * Bootstrap Tooltip class
29607 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29608 * to determine which dom element triggers the tooltip.
29610 * It needs to add support for additional attributes like tooltip-position
29613 * Create a new Toolti
29614 * @param {Object} config The config object
29617 Roo.bootstrap.Tooltip = function(config){
29618 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29620 this.alignment = Roo.bootstrap.Tooltip.alignment;
29622 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29623 this.alignment = config.alignment;
29628 Roo.apply(Roo.bootstrap.Tooltip, {
29630 * @function init initialize tooltip monitoring.
29634 currentTip : false,
29635 currentRegion : false,
29641 Roo.get(document).on('mouseover', this.enter ,this);
29642 Roo.get(document).on('mouseout', this.leave, this);
29645 this.currentTip = new Roo.bootstrap.Tooltip();
29648 enter : function(ev)
29650 var dom = ev.getTarget();
29652 //Roo.log(['enter',dom]);
29653 var el = Roo.fly(dom);
29654 if (this.currentEl) {
29656 //Roo.log(this.currentEl);
29657 //Roo.log(this.currentEl.contains(dom));
29658 if (this.currentEl == el) {
29661 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29667 if (this.currentTip.el) {
29668 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29672 if(!el || el.dom == document){
29678 if (!el.attr('tooltip')) {
29679 pel = el.findParent("[tooltip]");
29681 bindEl = Roo.get(pel);
29687 // you can not look for children, as if el is the body.. then everythign is the child..
29688 if (!pel && !el.attr('tooltip')) { //
29689 if (!el.select("[tooltip]").elements.length) {
29692 // is the mouse over this child...?
29693 bindEl = el.select("[tooltip]").first();
29694 var xy = ev.getXY();
29695 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29696 //Roo.log("not in region.");
29699 //Roo.log("child element over..");
29702 this.currentEl = el;
29703 this.currentTip.bind(bindEl);
29704 this.currentRegion = Roo.lib.Region.getRegion(dom);
29705 this.currentTip.enter();
29708 leave : function(ev)
29710 var dom = ev.getTarget();
29711 //Roo.log(['leave',dom]);
29712 if (!this.currentEl) {
29717 if (dom != this.currentEl.dom) {
29720 var xy = ev.getXY();
29721 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29724 // only activate leave if mouse cursor is outside... bounding box..
29729 if (this.currentTip) {
29730 this.currentTip.leave();
29732 //Roo.log('clear currentEl');
29733 this.currentEl = false;
29738 'left' : ['r-l', [-2,0], 'right'],
29739 'right' : ['l-r', [2,0], 'left'],
29740 'bottom' : ['t-b', [0,2], 'top'],
29741 'top' : [ 'b-t', [0,-2], 'bottom']
29747 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29752 delay : null, // can be { show : 300 , hide: 500}
29756 hoverState : null, //???
29758 placement : 'bottom',
29762 getAutoCreate : function(){
29769 cls : 'tooltip-arrow arrow'
29772 cls : 'tooltip-inner'
29779 bind : function(el)
29784 initEvents : function()
29786 this.arrowEl = this.el.select('.arrow', true).first();
29787 this.innerEl = this.el.select('.tooltip-inner', true).first();
29790 enter : function () {
29792 if (this.timeout != null) {
29793 clearTimeout(this.timeout);
29796 this.hoverState = 'in';
29797 //Roo.log("enter - show");
29798 if (!this.delay || !this.delay.show) {
29803 this.timeout = setTimeout(function () {
29804 if (_t.hoverState == 'in') {
29807 }, this.delay.show);
29811 clearTimeout(this.timeout);
29813 this.hoverState = 'out';
29814 if (!this.delay || !this.delay.hide) {
29820 this.timeout = setTimeout(function () {
29821 //Roo.log("leave - timeout");
29823 if (_t.hoverState == 'out') {
29825 Roo.bootstrap.Tooltip.currentEl = false;
29830 show : function (msg)
29833 this.render(document.body);
29836 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29838 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29840 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29842 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29843 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29845 var placement = typeof this.placement == 'function' ?
29846 this.placement.call(this, this.el, on_el) :
29849 var autoToken = /\s?auto?\s?/i;
29850 var autoPlace = autoToken.test(placement);
29852 placement = placement.replace(autoToken, '') || 'top';
29856 //this.el.setXY([0,0]);
29858 //this.el.dom.style.display='block';
29860 //this.el.appendTo(on_el);
29862 var p = this.getPosition();
29863 var box = this.el.getBox();
29869 var align = this.alignment[placement];
29871 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29873 if(placement == 'top' || placement == 'bottom'){
29875 placement = 'right';
29878 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29879 placement = 'left';
29882 var scroll = Roo.select('body', true).first().getScroll();
29884 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29888 align = this.alignment[placement];
29890 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29894 var elems = document.getElementsByTagName('div');
29895 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29896 for (var i = 0; i < elems.length; i++) {
29897 var zindex = Number.parseInt(
29898 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29901 if (zindex > highest) {
29908 this.el.dom.style.zIndex = highest;
29910 this.el.alignTo(this.bindEl, align[0],align[1]);
29911 //var arrow = this.el.select('.arrow',true).first();
29912 //arrow.set(align[2],
29914 this.el.addClass(placement);
29915 this.el.addClass("bs-tooltip-"+ placement);
29917 this.el.addClass('in fade show');
29919 this.hoverState = null;
29921 if (this.el.hasClass('fade')) {
29936 //this.el.setXY([0,0]);
29937 this.el.removeClass(['show', 'in']);
29953 * @class Roo.bootstrap.LocationPicker
29954 * @extends Roo.bootstrap.Component
29955 * Bootstrap LocationPicker class
29956 * @cfg {Number} latitude Position when init default 0
29957 * @cfg {Number} longitude Position when init default 0
29958 * @cfg {Number} zoom default 15
29959 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29960 * @cfg {Boolean} mapTypeControl default false
29961 * @cfg {Boolean} disableDoubleClickZoom default false
29962 * @cfg {Boolean} scrollwheel default true
29963 * @cfg {Boolean} streetViewControl default false
29964 * @cfg {Number} radius default 0
29965 * @cfg {String} locationName
29966 * @cfg {Boolean} draggable default true
29967 * @cfg {Boolean} enableAutocomplete default false
29968 * @cfg {Boolean} enableReverseGeocode default true
29969 * @cfg {String} markerTitle
29972 * Create a new LocationPicker
29973 * @param {Object} config The config object
29977 Roo.bootstrap.LocationPicker = function(config){
29979 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29984 * Fires when the picker initialized.
29985 * @param {Roo.bootstrap.LocationPicker} this
29986 * @param {Google Location} location
29990 * @event positionchanged
29991 * Fires when the picker position changed.
29992 * @param {Roo.bootstrap.LocationPicker} this
29993 * @param {Google Location} location
29995 positionchanged : true,
29998 * Fires when the map resize.
29999 * @param {Roo.bootstrap.LocationPicker} this
30004 * Fires when the map show.
30005 * @param {Roo.bootstrap.LocationPicker} this
30010 * Fires when the map hide.
30011 * @param {Roo.bootstrap.LocationPicker} this
30016 * Fires when click the map.
30017 * @param {Roo.bootstrap.LocationPicker} this
30018 * @param {Map event} e
30022 * @event mapRightClick
30023 * Fires when right click the map.
30024 * @param {Roo.bootstrap.LocationPicker} this
30025 * @param {Map event} e
30027 mapRightClick : true,
30029 * @event markerClick
30030 * Fires when click the marker.
30031 * @param {Roo.bootstrap.LocationPicker} this
30032 * @param {Map event} e
30034 markerClick : true,
30036 * @event markerRightClick
30037 * Fires when right click the marker.
30038 * @param {Roo.bootstrap.LocationPicker} this
30039 * @param {Map event} e
30041 markerRightClick : true,
30043 * @event OverlayViewDraw
30044 * Fires when OverlayView Draw
30045 * @param {Roo.bootstrap.LocationPicker} this
30047 OverlayViewDraw : true,
30049 * @event OverlayViewOnAdd
30050 * Fires when OverlayView Draw
30051 * @param {Roo.bootstrap.LocationPicker} this
30053 OverlayViewOnAdd : true,
30055 * @event OverlayViewOnRemove
30056 * Fires when OverlayView Draw
30057 * @param {Roo.bootstrap.LocationPicker} this
30059 OverlayViewOnRemove : true,
30061 * @event OverlayViewShow
30062 * Fires when OverlayView Draw
30063 * @param {Roo.bootstrap.LocationPicker} this
30064 * @param {Pixel} cpx
30066 OverlayViewShow : true,
30068 * @event OverlayViewHide
30069 * Fires when OverlayView Draw
30070 * @param {Roo.bootstrap.LocationPicker} this
30072 OverlayViewHide : true,
30074 * @event loadexception
30075 * Fires when load google lib failed.
30076 * @param {Roo.bootstrap.LocationPicker} this
30078 loadexception : true
30083 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30085 gMapContext: false,
30091 mapTypeControl: false,
30092 disableDoubleClickZoom: false,
30094 streetViewControl: false,
30098 enableAutocomplete: false,
30099 enableReverseGeocode: true,
30102 getAutoCreate: function()
30107 cls: 'roo-location-picker'
30113 initEvents: function(ct, position)
30115 if(!this.el.getWidth() || this.isApplied()){
30119 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30124 initial: function()
30126 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30127 this.fireEvent('loadexception', this);
30131 if(!this.mapTypeId){
30132 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30135 this.gMapContext = this.GMapContext();
30137 this.initOverlayView();
30139 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30143 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30144 _this.setPosition(_this.gMapContext.marker.position);
30147 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30148 _this.fireEvent('mapClick', this, event);
30152 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30153 _this.fireEvent('mapRightClick', this, event);
30157 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30158 _this.fireEvent('markerClick', this, event);
30162 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30163 _this.fireEvent('markerRightClick', this, event);
30167 this.setPosition(this.gMapContext.location);
30169 this.fireEvent('initial', this, this.gMapContext.location);
30172 initOverlayView: function()
30176 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30180 _this.fireEvent('OverlayViewDraw', _this);
30185 _this.fireEvent('OverlayViewOnAdd', _this);
30188 onRemove: function()
30190 _this.fireEvent('OverlayViewOnRemove', _this);
30193 show: function(cpx)
30195 _this.fireEvent('OverlayViewShow', _this, cpx);
30200 _this.fireEvent('OverlayViewHide', _this);
30206 fromLatLngToContainerPixel: function(event)
30208 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30211 isApplied: function()
30213 return this.getGmapContext() == false ? false : true;
30216 getGmapContext: function()
30218 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30221 GMapContext: function()
30223 var position = new google.maps.LatLng(this.latitude, this.longitude);
30225 var _map = new google.maps.Map(this.el.dom, {
30228 mapTypeId: this.mapTypeId,
30229 mapTypeControl: this.mapTypeControl,
30230 disableDoubleClickZoom: this.disableDoubleClickZoom,
30231 scrollwheel: this.scrollwheel,
30232 streetViewControl: this.streetViewControl,
30233 locationName: this.locationName,
30234 draggable: this.draggable,
30235 enableAutocomplete: this.enableAutocomplete,
30236 enableReverseGeocode: this.enableReverseGeocode
30239 var _marker = new google.maps.Marker({
30240 position: position,
30242 title: this.markerTitle,
30243 draggable: this.draggable
30250 location: position,
30251 radius: this.radius,
30252 locationName: this.locationName,
30253 addressComponents: {
30254 formatted_address: null,
30255 addressLine1: null,
30256 addressLine2: null,
30258 streetNumber: null,
30262 stateOrProvince: null
30265 domContainer: this.el.dom,
30266 geodecoder: new google.maps.Geocoder()
30270 drawCircle: function(center, radius, options)
30272 if (this.gMapContext.circle != null) {
30273 this.gMapContext.circle.setMap(null);
30277 options = Roo.apply({}, options, {
30278 strokeColor: "#0000FF",
30279 strokeOpacity: .35,
30281 fillColor: "#0000FF",
30285 options.map = this.gMapContext.map;
30286 options.radius = radius;
30287 options.center = center;
30288 this.gMapContext.circle = new google.maps.Circle(options);
30289 return this.gMapContext.circle;
30295 setPosition: function(location)
30297 this.gMapContext.location = location;
30298 this.gMapContext.marker.setPosition(location);
30299 this.gMapContext.map.panTo(location);
30300 this.drawCircle(location, this.gMapContext.radius, {});
30304 if (this.gMapContext.settings.enableReverseGeocode) {
30305 this.gMapContext.geodecoder.geocode({
30306 latLng: this.gMapContext.location
30307 }, function(results, status) {
30309 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30310 _this.gMapContext.locationName = results[0].formatted_address;
30311 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30313 _this.fireEvent('positionchanged', this, location);
30320 this.fireEvent('positionchanged', this, location);
30325 google.maps.event.trigger(this.gMapContext.map, "resize");
30327 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30329 this.fireEvent('resize', this);
30332 setPositionByLatLng: function(latitude, longitude)
30334 this.setPosition(new google.maps.LatLng(latitude, longitude));
30337 getCurrentPosition: function()
30340 latitude: this.gMapContext.location.lat(),
30341 longitude: this.gMapContext.location.lng()
30345 getAddressName: function()
30347 return this.gMapContext.locationName;
30350 getAddressComponents: function()
30352 return this.gMapContext.addressComponents;
30355 address_component_from_google_geocode: function(address_components)
30359 for (var i = 0; i < address_components.length; i++) {
30360 var component = address_components[i];
30361 if (component.types.indexOf("postal_code") >= 0) {
30362 result.postalCode = component.short_name;
30363 } else if (component.types.indexOf("street_number") >= 0) {
30364 result.streetNumber = component.short_name;
30365 } else if (component.types.indexOf("route") >= 0) {
30366 result.streetName = component.short_name;
30367 } else if (component.types.indexOf("neighborhood") >= 0) {
30368 result.city = component.short_name;
30369 } else if (component.types.indexOf("locality") >= 0) {
30370 result.city = component.short_name;
30371 } else if (component.types.indexOf("sublocality") >= 0) {
30372 result.district = component.short_name;
30373 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30374 result.stateOrProvince = component.short_name;
30375 } else if (component.types.indexOf("country") >= 0) {
30376 result.country = component.short_name;
30380 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30381 result.addressLine2 = "";
30385 setZoomLevel: function(zoom)
30387 this.gMapContext.map.setZoom(zoom);
30400 this.fireEvent('show', this);
30411 this.fireEvent('hide', this);
30416 Roo.apply(Roo.bootstrap.LocationPicker, {
30418 OverlayView : function(map, options)
30420 options = options || {};
30427 * @class Roo.bootstrap.Alert
30428 * @extends Roo.bootstrap.Component
30429 * Bootstrap Alert class - shows an alert area box
30431 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30432 Enter a valid email address
30435 * @cfg {String} title The title of alert
30436 * @cfg {String} html The content of alert
30437 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30438 * @cfg {String} fa font-awesomeicon
30439 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30440 * @cfg {Boolean} close true to show a x closer
30444 * Create a new alert
30445 * @param {Object} config The config object
30449 Roo.bootstrap.Alert = function(config){
30450 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30454 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30460 faicon: false, // BC
30464 getAutoCreate : function()
30476 style : this.close ? '' : 'display:none'
30480 cls : 'roo-alert-icon'
30485 cls : 'roo-alert-title',
30490 cls : 'roo-alert-text',
30497 cfg.cn[0].cls += ' fa ' + this.faicon;
30500 cfg.cn[0].cls += ' fa ' + this.fa;
30504 cfg.cls += ' alert-' + this.weight;
30510 initEvents: function()
30512 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30513 this.titleEl = this.el.select('.roo-alert-title',true).first();
30514 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30515 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30516 if (this.seconds > 0) {
30517 this.hide.defer(this.seconds, this);
30521 * Set the Title Message HTML
30522 * @param {String} html
30524 setTitle : function(str)
30526 this.titleEl.dom.innerHTML = str;
30530 * Set the Body Message HTML
30531 * @param {String} html
30533 setHtml : function(str)
30535 this.htmlEl.dom.innerHTML = str;
30538 * Set the Weight of the alert
30539 * @param {String} (success|info|warning|danger) weight
30542 setWeight : function(weight)
30545 this.el.removeClass('alert-' + this.weight);
30548 this.weight = weight;
30550 this.el.addClass('alert-' + this.weight);
30553 * Set the Icon of the alert
30554 * @param {String} see fontawsome names (name without the 'fa-' bit)
30556 setIcon : function(icon)
30559 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30562 this.faicon = icon;
30564 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30589 * @class Roo.bootstrap.UploadCropbox
30590 * @extends Roo.bootstrap.Component
30591 * Bootstrap UploadCropbox class
30592 * @cfg {String} emptyText show when image has been loaded
30593 * @cfg {String} rotateNotify show when image too small to rotate
30594 * @cfg {Number} errorTimeout default 3000
30595 * @cfg {Number} minWidth default 300
30596 * @cfg {Number} minHeight default 300
30597 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30598 * @cfg {Boolean} isDocument (true|false) default false
30599 * @cfg {String} url action url
30600 * @cfg {String} paramName default 'imageUpload'
30601 * @cfg {String} method default POST
30602 * @cfg {Boolean} loadMask (true|false) default true
30603 * @cfg {Boolean} loadingText default 'Loading...'
30606 * Create a new UploadCropbox
30607 * @param {Object} config The config object
30610 Roo.bootstrap.UploadCropbox = function(config){
30611 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30615 * @event beforeselectfile
30616 * Fire before select file
30617 * @param {Roo.bootstrap.UploadCropbox} this
30619 "beforeselectfile" : true,
30622 * Fire after initEvent
30623 * @param {Roo.bootstrap.UploadCropbox} this
30628 * Fire after initEvent
30629 * @param {Roo.bootstrap.UploadCropbox} this
30630 * @param {String} data
30635 * Fire when preparing the file data
30636 * @param {Roo.bootstrap.UploadCropbox} this
30637 * @param {Object} file
30642 * Fire when get exception
30643 * @param {Roo.bootstrap.UploadCropbox} this
30644 * @param {XMLHttpRequest} xhr
30646 "exception" : true,
30648 * @event beforeloadcanvas
30649 * Fire before load the canvas
30650 * @param {Roo.bootstrap.UploadCropbox} this
30651 * @param {String} src
30653 "beforeloadcanvas" : true,
30656 * Fire when trash image
30657 * @param {Roo.bootstrap.UploadCropbox} this
30662 * Fire when download the image
30663 * @param {Roo.bootstrap.UploadCropbox} this
30667 * @event footerbuttonclick
30668 * Fire when footerbuttonclick
30669 * @param {Roo.bootstrap.UploadCropbox} this
30670 * @param {String} type
30672 "footerbuttonclick" : true,
30676 * @param {Roo.bootstrap.UploadCropbox} this
30681 * Fire when rotate the image
30682 * @param {Roo.bootstrap.UploadCropbox} this
30683 * @param {String} pos
30688 * Fire when inspect the file
30689 * @param {Roo.bootstrap.UploadCropbox} this
30690 * @param {Object} file
30695 * Fire when xhr upload the file
30696 * @param {Roo.bootstrap.UploadCropbox} this
30697 * @param {Object} data
30702 * Fire when arrange the file data
30703 * @param {Roo.bootstrap.UploadCropbox} this
30704 * @param {Object} formData
30709 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30712 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30714 emptyText : 'Click to upload image',
30715 rotateNotify : 'Image is too small to rotate',
30716 errorTimeout : 3000,
30730 cropType : 'image/jpeg',
30732 canvasLoaded : false,
30733 isDocument : false,
30735 paramName : 'imageUpload',
30737 loadingText : 'Loading...',
30740 getAutoCreate : function()
30744 cls : 'roo-upload-cropbox',
30748 cls : 'roo-upload-cropbox-selector',
30753 cls : 'roo-upload-cropbox-body',
30754 style : 'cursor:pointer',
30758 cls : 'roo-upload-cropbox-preview'
30762 cls : 'roo-upload-cropbox-thumb'
30766 cls : 'roo-upload-cropbox-empty-notify',
30767 html : this.emptyText
30771 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30772 html : this.rotateNotify
30778 cls : 'roo-upload-cropbox-footer',
30781 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30791 onRender : function(ct, position)
30793 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30795 if (this.buttons.length) {
30797 Roo.each(this.buttons, function(bb) {
30799 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30801 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30807 this.maskEl = this.el;
30811 initEvents : function()
30813 this.urlAPI = (window.createObjectURL && window) ||
30814 (window.URL && URL.revokeObjectURL && URL) ||
30815 (window.webkitURL && webkitURL);
30817 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30818 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30820 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30821 this.selectorEl.hide();
30823 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30824 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30826 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30827 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30828 this.thumbEl.hide();
30830 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30831 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30833 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30834 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30835 this.errorEl.hide();
30837 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30838 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30839 this.footerEl.hide();
30841 this.setThumbBoxSize();
30847 this.fireEvent('initial', this);
30854 window.addEventListener("resize", function() { _this.resize(); } );
30856 this.bodyEl.on('click', this.beforeSelectFile, this);
30859 this.bodyEl.on('touchstart', this.onTouchStart, this);
30860 this.bodyEl.on('touchmove', this.onTouchMove, this);
30861 this.bodyEl.on('touchend', this.onTouchEnd, this);
30865 this.bodyEl.on('mousedown', this.onMouseDown, this);
30866 this.bodyEl.on('mousemove', this.onMouseMove, this);
30867 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30868 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30869 Roo.get(document).on('mouseup', this.onMouseUp, this);
30872 this.selectorEl.on('change', this.onFileSelected, this);
30878 this.baseScale = 1;
30880 this.baseRotate = 1;
30881 this.dragable = false;
30882 this.pinching = false;
30885 this.cropData = false;
30886 this.notifyEl.dom.innerHTML = this.emptyText;
30888 this.selectorEl.dom.value = '';
30892 resize : function()
30894 if(this.fireEvent('resize', this) != false){
30895 this.setThumbBoxPosition();
30896 this.setCanvasPosition();
30900 onFooterButtonClick : function(e, el, o, type)
30903 case 'rotate-left' :
30904 this.onRotateLeft(e);
30906 case 'rotate-right' :
30907 this.onRotateRight(e);
30910 this.beforeSelectFile(e);
30925 this.fireEvent('footerbuttonclick', this, type);
30928 beforeSelectFile : function(e)
30930 e.preventDefault();
30932 if(this.fireEvent('beforeselectfile', this) != false){
30933 this.selectorEl.dom.click();
30937 onFileSelected : function(e)
30939 e.preventDefault();
30941 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30945 var file = this.selectorEl.dom.files[0];
30947 if(this.fireEvent('inspect', this, file) != false){
30948 this.prepare(file);
30953 trash : function(e)
30955 this.fireEvent('trash', this);
30958 download : function(e)
30960 this.fireEvent('download', this);
30963 loadCanvas : function(src)
30965 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30969 this.imageEl = document.createElement('img');
30973 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30975 this.imageEl.src = src;
30979 onLoadCanvas : function()
30981 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30982 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30984 this.bodyEl.un('click', this.beforeSelectFile, this);
30986 this.notifyEl.hide();
30987 this.thumbEl.show();
30988 this.footerEl.show();
30990 this.baseRotateLevel();
30992 if(this.isDocument){
30993 this.setThumbBoxSize();
30996 this.setThumbBoxPosition();
30998 this.baseScaleLevel();
31004 this.canvasLoaded = true;
31007 this.maskEl.unmask();
31012 setCanvasPosition : function()
31014 if(!this.canvasEl){
31018 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31019 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31021 this.previewEl.setLeft(pw);
31022 this.previewEl.setTop(ph);
31026 onMouseDown : function(e)
31030 this.dragable = true;
31031 this.pinching = false;
31033 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31034 this.dragable = false;
31038 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31039 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31043 onMouseMove : function(e)
31047 if(!this.canvasLoaded){
31051 if (!this.dragable){
31055 var minX = Math.ceil(this.thumbEl.getLeft(true));
31056 var minY = Math.ceil(this.thumbEl.getTop(true));
31058 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31059 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31061 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31062 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31064 x = x - this.mouseX;
31065 y = y - this.mouseY;
31067 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31068 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31070 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31071 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31073 this.previewEl.setLeft(bgX);
31074 this.previewEl.setTop(bgY);
31076 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31077 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31080 onMouseUp : function(e)
31084 this.dragable = false;
31087 onMouseWheel : function(e)
31091 this.startScale = this.scale;
31093 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31095 if(!this.zoomable()){
31096 this.scale = this.startScale;
31105 zoomable : function()
31107 var minScale = this.thumbEl.getWidth() / this.minWidth;
31109 if(this.minWidth < this.minHeight){
31110 minScale = this.thumbEl.getHeight() / this.minHeight;
31113 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31114 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31118 (this.rotate == 0 || this.rotate == 180) &&
31120 width > this.imageEl.OriginWidth ||
31121 height > this.imageEl.OriginHeight ||
31122 (width < this.minWidth && height < this.minHeight)
31130 (this.rotate == 90 || this.rotate == 270) &&
31132 width > this.imageEl.OriginWidth ||
31133 height > this.imageEl.OriginHeight ||
31134 (width < this.minHeight && height < this.minWidth)
31141 !this.isDocument &&
31142 (this.rotate == 0 || this.rotate == 180) &&
31144 width < this.minWidth ||
31145 width > this.imageEl.OriginWidth ||
31146 height < this.minHeight ||
31147 height > this.imageEl.OriginHeight
31154 !this.isDocument &&
31155 (this.rotate == 90 || this.rotate == 270) &&
31157 width < this.minHeight ||
31158 width > this.imageEl.OriginWidth ||
31159 height < this.minWidth ||
31160 height > this.imageEl.OriginHeight
31170 onRotateLeft : function(e)
31172 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31174 var minScale = this.thumbEl.getWidth() / this.minWidth;
31176 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31177 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31179 this.startScale = this.scale;
31181 while (this.getScaleLevel() < minScale){
31183 this.scale = this.scale + 1;
31185 if(!this.zoomable()){
31190 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31191 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31196 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31203 this.scale = this.startScale;
31205 this.onRotateFail();
31210 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31212 if(this.isDocument){
31213 this.setThumbBoxSize();
31214 this.setThumbBoxPosition();
31215 this.setCanvasPosition();
31220 this.fireEvent('rotate', this, 'left');
31224 onRotateRight : function(e)
31226 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31228 var minScale = this.thumbEl.getWidth() / this.minWidth;
31230 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31231 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31233 this.startScale = this.scale;
31235 while (this.getScaleLevel() < minScale){
31237 this.scale = this.scale + 1;
31239 if(!this.zoomable()){
31244 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31245 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31250 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31257 this.scale = this.startScale;
31259 this.onRotateFail();
31264 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31266 if(this.isDocument){
31267 this.setThumbBoxSize();
31268 this.setThumbBoxPosition();
31269 this.setCanvasPosition();
31274 this.fireEvent('rotate', this, 'right');
31277 onRotateFail : function()
31279 this.errorEl.show(true);
31283 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31288 this.previewEl.dom.innerHTML = '';
31290 var canvasEl = document.createElement("canvas");
31292 var contextEl = canvasEl.getContext("2d");
31294 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31295 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31296 var center = this.imageEl.OriginWidth / 2;
31298 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31299 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31300 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31301 center = this.imageEl.OriginHeight / 2;
31304 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31306 contextEl.translate(center, center);
31307 contextEl.rotate(this.rotate * Math.PI / 180);
31309 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31311 this.canvasEl = document.createElement("canvas");
31313 this.contextEl = this.canvasEl.getContext("2d");
31315 switch (this.rotate) {
31318 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31319 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31321 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31326 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31327 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31329 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31330 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);
31334 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31339 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31340 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31342 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31343 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);
31347 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);
31352 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31353 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31355 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31356 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31360 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);
31367 this.previewEl.appendChild(this.canvasEl);
31369 this.setCanvasPosition();
31374 if(!this.canvasLoaded){
31378 var imageCanvas = document.createElement("canvas");
31380 var imageContext = imageCanvas.getContext("2d");
31382 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31383 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31385 var center = imageCanvas.width / 2;
31387 imageContext.translate(center, center);
31389 imageContext.rotate(this.rotate * Math.PI / 180);
31391 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31393 var canvas = document.createElement("canvas");
31395 var context = canvas.getContext("2d");
31397 canvas.width = this.minWidth;
31398 canvas.height = this.minHeight;
31400 switch (this.rotate) {
31403 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31404 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31406 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31407 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31409 var targetWidth = this.minWidth - 2 * x;
31410 var targetHeight = this.minHeight - 2 * y;
31414 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31415 scale = targetWidth / width;
31418 if(x > 0 && y == 0){
31419 scale = targetHeight / height;
31422 if(x > 0 && y > 0){
31423 scale = targetWidth / width;
31425 if(width < height){
31426 scale = targetHeight / height;
31430 context.scale(scale, scale);
31432 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31433 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31435 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31436 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31438 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31443 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31444 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31446 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31447 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31449 var targetWidth = this.minWidth - 2 * x;
31450 var targetHeight = this.minHeight - 2 * y;
31454 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31455 scale = targetWidth / width;
31458 if(x > 0 && y == 0){
31459 scale = targetHeight / height;
31462 if(x > 0 && y > 0){
31463 scale = targetWidth / width;
31465 if(width < height){
31466 scale = targetHeight / height;
31470 context.scale(scale, scale);
31472 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31473 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31475 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31476 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31478 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31480 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31485 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31486 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31488 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31489 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31491 var targetWidth = this.minWidth - 2 * x;
31492 var targetHeight = this.minHeight - 2 * y;
31496 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31497 scale = targetWidth / width;
31500 if(x > 0 && y == 0){
31501 scale = targetHeight / height;
31504 if(x > 0 && y > 0){
31505 scale = targetWidth / width;
31507 if(width < height){
31508 scale = targetHeight / height;
31512 context.scale(scale, scale);
31514 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31515 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31517 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31518 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31520 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31521 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31523 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31528 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31529 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31531 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31532 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31534 var targetWidth = this.minWidth - 2 * x;
31535 var targetHeight = this.minHeight - 2 * y;
31539 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31540 scale = targetWidth / width;
31543 if(x > 0 && y == 0){
31544 scale = targetHeight / height;
31547 if(x > 0 && y > 0){
31548 scale = targetWidth / width;
31550 if(width < height){
31551 scale = targetHeight / height;
31555 context.scale(scale, scale);
31557 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31558 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31560 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31561 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31563 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31565 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31572 this.cropData = canvas.toDataURL(this.cropType);
31574 if(this.fireEvent('crop', this, this.cropData) !== false){
31575 this.process(this.file, this.cropData);
31582 setThumbBoxSize : function()
31586 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31587 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31588 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31590 this.minWidth = width;
31591 this.minHeight = height;
31593 if(this.rotate == 90 || this.rotate == 270){
31594 this.minWidth = height;
31595 this.minHeight = width;
31600 width = Math.ceil(this.minWidth * height / this.minHeight);
31602 if(this.minWidth > this.minHeight){
31604 height = Math.ceil(this.minHeight * width / this.minWidth);
31607 this.thumbEl.setStyle({
31608 width : width + 'px',
31609 height : height + 'px'
31616 setThumbBoxPosition : function()
31618 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31619 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31621 this.thumbEl.setLeft(x);
31622 this.thumbEl.setTop(y);
31626 baseRotateLevel : function()
31628 this.baseRotate = 1;
31631 typeof(this.exif) != 'undefined' &&
31632 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31633 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31635 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31638 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31642 baseScaleLevel : function()
31646 if(this.isDocument){
31648 if(this.baseRotate == 6 || this.baseRotate == 8){
31650 height = this.thumbEl.getHeight();
31651 this.baseScale = height / this.imageEl.OriginWidth;
31653 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31654 width = this.thumbEl.getWidth();
31655 this.baseScale = width / this.imageEl.OriginHeight;
31661 height = this.thumbEl.getHeight();
31662 this.baseScale = height / this.imageEl.OriginHeight;
31664 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31665 width = this.thumbEl.getWidth();
31666 this.baseScale = width / this.imageEl.OriginWidth;
31672 if(this.baseRotate == 6 || this.baseRotate == 8){
31674 width = this.thumbEl.getHeight();
31675 this.baseScale = width / this.imageEl.OriginHeight;
31677 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31678 height = this.thumbEl.getWidth();
31679 this.baseScale = height / this.imageEl.OriginHeight;
31682 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31683 height = this.thumbEl.getWidth();
31684 this.baseScale = height / this.imageEl.OriginHeight;
31686 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31687 width = this.thumbEl.getHeight();
31688 this.baseScale = width / this.imageEl.OriginWidth;
31695 width = this.thumbEl.getWidth();
31696 this.baseScale = width / this.imageEl.OriginWidth;
31698 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31699 height = this.thumbEl.getHeight();
31700 this.baseScale = height / this.imageEl.OriginHeight;
31703 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31705 height = this.thumbEl.getHeight();
31706 this.baseScale = height / this.imageEl.OriginHeight;
31708 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31709 width = this.thumbEl.getWidth();
31710 this.baseScale = width / this.imageEl.OriginWidth;
31718 getScaleLevel : function()
31720 return this.baseScale * Math.pow(1.1, this.scale);
31723 onTouchStart : function(e)
31725 if(!this.canvasLoaded){
31726 this.beforeSelectFile(e);
31730 var touches = e.browserEvent.touches;
31736 if(touches.length == 1){
31737 this.onMouseDown(e);
31741 if(touches.length != 2){
31747 for(var i = 0, finger; finger = touches[i]; i++){
31748 coords.push(finger.pageX, finger.pageY);
31751 var x = Math.pow(coords[0] - coords[2], 2);
31752 var y = Math.pow(coords[1] - coords[3], 2);
31754 this.startDistance = Math.sqrt(x + y);
31756 this.startScale = this.scale;
31758 this.pinching = true;
31759 this.dragable = false;
31763 onTouchMove : function(e)
31765 if(!this.pinching && !this.dragable){
31769 var touches = e.browserEvent.touches;
31776 this.onMouseMove(e);
31782 for(var i = 0, finger; finger = touches[i]; i++){
31783 coords.push(finger.pageX, finger.pageY);
31786 var x = Math.pow(coords[0] - coords[2], 2);
31787 var y = Math.pow(coords[1] - coords[3], 2);
31789 this.endDistance = Math.sqrt(x + y);
31791 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31793 if(!this.zoomable()){
31794 this.scale = this.startScale;
31802 onTouchEnd : function(e)
31804 this.pinching = false;
31805 this.dragable = false;
31809 process : function(file, crop)
31812 this.maskEl.mask(this.loadingText);
31815 this.xhr = new XMLHttpRequest();
31817 file.xhr = this.xhr;
31819 this.xhr.open(this.method, this.url, true);
31822 "Accept": "application/json",
31823 "Cache-Control": "no-cache",
31824 "X-Requested-With": "XMLHttpRequest"
31827 for (var headerName in headers) {
31828 var headerValue = headers[headerName];
31830 this.xhr.setRequestHeader(headerName, headerValue);
31836 this.xhr.onload = function()
31838 _this.xhrOnLoad(_this.xhr);
31841 this.xhr.onerror = function()
31843 _this.xhrOnError(_this.xhr);
31846 var formData = new FormData();
31848 formData.append('returnHTML', 'NO');
31851 formData.append('crop', crop);
31854 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31855 formData.append(this.paramName, file, file.name);
31858 if(typeof(file.filename) != 'undefined'){
31859 formData.append('filename', file.filename);
31862 if(typeof(file.mimetype) != 'undefined'){
31863 formData.append('mimetype', file.mimetype);
31866 if(this.fireEvent('arrange', this, formData) != false){
31867 this.xhr.send(formData);
31871 xhrOnLoad : function(xhr)
31874 this.maskEl.unmask();
31877 if (xhr.readyState !== 4) {
31878 this.fireEvent('exception', this, xhr);
31882 var response = Roo.decode(xhr.responseText);
31884 if(!response.success){
31885 this.fireEvent('exception', this, xhr);
31889 var response = Roo.decode(xhr.responseText);
31891 this.fireEvent('upload', this, response);
31895 xhrOnError : function()
31898 this.maskEl.unmask();
31901 Roo.log('xhr on error');
31903 var response = Roo.decode(xhr.responseText);
31909 prepare : function(file)
31912 this.maskEl.mask(this.loadingText);
31918 if(typeof(file) === 'string'){
31919 this.loadCanvas(file);
31923 if(!file || !this.urlAPI){
31928 this.cropType = file.type;
31932 if(this.fireEvent('prepare', this, this.file) != false){
31934 var reader = new FileReader();
31936 reader.onload = function (e) {
31937 if (e.target.error) {
31938 Roo.log(e.target.error);
31942 var buffer = e.target.result,
31943 dataView = new DataView(buffer),
31945 maxOffset = dataView.byteLength - 4,
31949 if (dataView.getUint16(0) === 0xffd8) {
31950 while (offset < maxOffset) {
31951 markerBytes = dataView.getUint16(offset);
31953 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31954 markerLength = dataView.getUint16(offset + 2) + 2;
31955 if (offset + markerLength > dataView.byteLength) {
31956 Roo.log('Invalid meta data: Invalid segment size.');
31960 if(markerBytes == 0xffe1){
31961 _this.parseExifData(
31968 offset += markerLength;
31978 var url = _this.urlAPI.createObjectURL(_this.file);
31980 _this.loadCanvas(url);
31985 reader.readAsArrayBuffer(this.file);
31991 parseExifData : function(dataView, offset, length)
31993 var tiffOffset = offset + 10,
31997 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31998 // No Exif data, might be XMP data instead
32002 // Check for the ASCII code for "Exif" (0x45786966):
32003 if (dataView.getUint32(offset + 4) !== 0x45786966) {
32004 // No Exif data, might be XMP data instead
32007 if (tiffOffset + 8 > dataView.byteLength) {
32008 Roo.log('Invalid Exif data: Invalid segment size.');
32011 // Check for the two null bytes:
32012 if (dataView.getUint16(offset + 8) !== 0x0000) {
32013 Roo.log('Invalid Exif data: Missing byte alignment offset.');
32016 // Check the byte alignment:
32017 switch (dataView.getUint16(tiffOffset)) {
32019 littleEndian = true;
32022 littleEndian = false;
32025 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32028 // Check for the TIFF tag marker (0x002A):
32029 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32030 Roo.log('Invalid Exif data: Missing TIFF marker.');
32033 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32034 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32036 this.parseExifTags(
32039 tiffOffset + dirOffset,
32044 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32049 if (dirOffset + 6 > dataView.byteLength) {
32050 Roo.log('Invalid Exif data: Invalid directory offset.');
32053 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32054 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32055 if (dirEndOffset + 4 > dataView.byteLength) {
32056 Roo.log('Invalid Exif data: Invalid directory size.');
32059 for (i = 0; i < tagsNumber; i += 1) {
32063 dirOffset + 2 + 12 * i, // tag offset
32067 // Return the offset to the next directory:
32068 return dataView.getUint32(dirEndOffset, littleEndian);
32071 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32073 var tag = dataView.getUint16(offset, littleEndian);
32075 this.exif[tag] = this.getExifValue(
32079 dataView.getUint16(offset + 2, littleEndian), // tag type
32080 dataView.getUint32(offset + 4, littleEndian), // tag length
32085 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32087 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32096 Roo.log('Invalid Exif data: Invalid tag type.');
32100 tagSize = tagType.size * length;
32101 // Determine if the value is contained in the dataOffset bytes,
32102 // or if the value at the dataOffset is a pointer to the actual data:
32103 dataOffset = tagSize > 4 ?
32104 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32105 if (dataOffset + tagSize > dataView.byteLength) {
32106 Roo.log('Invalid Exif data: Invalid data offset.');
32109 if (length === 1) {
32110 return tagType.getValue(dataView, dataOffset, littleEndian);
32113 for (i = 0; i < length; i += 1) {
32114 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32117 if (tagType.ascii) {
32119 // Concatenate the chars:
32120 for (i = 0; i < values.length; i += 1) {
32122 // Ignore the terminating NULL byte(s):
32123 if (c === '\u0000') {
32135 Roo.apply(Roo.bootstrap.UploadCropbox, {
32137 'Orientation': 0x0112
32141 1: 0, //'top-left',
32143 3: 180, //'bottom-right',
32144 // 4: 'bottom-left',
32146 6: 90, //'right-top',
32147 // 7: 'right-bottom',
32148 8: 270 //'left-bottom'
32152 // byte, 8-bit unsigned int:
32154 getValue: function (dataView, dataOffset) {
32155 return dataView.getUint8(dataOffset);
32159 // ascii, 8-bit byte:
32161 getValue: function (dataView, dataOffset) {
32162 return String.fromCharCode(dataView.getUint8(dataOffset));
32167 // short, 16 bit int:
32169 getValue: function (dataView, dataOffset, littleEndian) {
32170 return dataView.getUint16(dataOffset, littleEndian);
32174 // long, 32 bit int:
32176 getValue: function (dataView, dataOffset, littleEndian) {
32177 return dataView.getUint32(dataOffset, littleEndian);
32181 // rational = two long values, first is numerator, second is denominator:
32183 getValue: function (dataView, dataOffset, littleEndian) {
32184 return dataView.getUint32(dataOffset, littleEndian) /
32185 dataView.getUint32(dataOffset + 4, littleEndian);
32189 // slong, 32 bit signed int:
32191 getValue: function (dataView, dataOffset, littleEndian) {
32192 return dataView.getInt32(dataOffset, littleEndian);
32196 // srational, two slongs, first is numerator, second is denominator:
32198 getValue: function (dataView, dataOffset, littleEndian) {
32199 return dataView.getInt32(dataOffset, littleEndian) /
32200 dataView.getInt32(dataOffset + 4, littleEndian);
32210 cls : 'btn-group roo-upload-cropbox-rotate-left',
32211 action : 'rotate-left',
32215 cls : 'btn btn-default',
32216 html : '<i class="fa fa-undo"></i>'
32222 cls : 'btn-group roo-upload-cropbox-picture',
32223 action : 'picture',
32227 cls : 'btn btn-default',
32228 html : '<i class="fa fa-picture-o"></i>'
32234 cls : 'btn-group roo-upload-cropbox-rotate-right',
32235 action : 'rotate-right',
32239 cls : 'btn btn-default',
32240 html : '<i class="fa fa-repeat"></i>'
32248 cls : 'btn-group roo-upload-cropbox-rotate-left',
32249 action : 'rotate-left',
32253 cls : 'btn btn-default',
32254 html : '<i class="fa fa-undo"></i>'
32260 cls : 'btn-group roo-upload-cropbox-download',
32261 action : 'download',
32265 cls : 'btn btn-default',
32266 html : '<i class="fa fa-download"></i>'
32272 cls : 'btn-group roo-upload-cropbox-crop',
32277 cls : 'btn btn-default',
32278 html : '<i class="fa fa-crop"></i>'
32284 cls : 'btn-group roo-upload-cropbox-trash',
32289 cls : 'btn btn-default',
32290 html : '<i class="fa fa-trash"></i>'
32296 cls : 'btn-group roo-upload-cropbox-rotate-right',
32297 action : 'rotate-right',
32301 cls : 'btn btn-default',
32302 html : '<i class="fa fa-repeat"></i>'
32310 cls : 'btn-group roo-upload-cropbox-rotate-left',
32311 action : 'rotate-left',
32315 cls : 'btn btn-default',
32316 html : '<i class="fa fa-undo"></i>'
32322 cls : 'btn-group roo-upload-cropbox-rotate-right',
32323 action : 'rotate-right',
32327 cls : 'btn btn-default',
32328 html : '<i class="fa fa-repeat"></i>'
32341 * @class Roo.bootstrap.DocumentManager
32342 * @extends Roo.bootstrap.Component
32343 * Bootstrap DocumentManager class
32344 * @cfg {String} paramName default 'imageUpload'
32345 * @cfg {String} toolTipName default 'filename'
32346 * @cfg {String} method default POST
32347 * @cfg {String} url action url
32348 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32349 * @cfg {Boolean} multiple multiple upload default true
32350 * @cfg {Number} thumbSize default 300
32351 * @cfg {String} fieldLabel
32352 * @cfg {Number} labelWidth default 4
32353 * @cfg {String} labelAlign (left|top) default left
32354 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32355 * @cfg {Number} labellg set the width of label (1-12)
32356 * @cfg {Number} labelmd set the width of label (1-12)
32357 * @cfg {Number} labelsm set the width of label (1-12)
32358 * @cfg {Number} labelxs set the width of label (1-12)
32361 * Create a new DocumentManager
32362 * @param {Object} config The config object
32365 Roo.bootstrap.DocumentManager = function(config){
32366 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32369 this.delegates = [];
32374 * Fire when initial the DocumentManager
32375 * @param {Roo.bootstrap.DocumentManager} this
32380 * inspect selected file
32381 * @param {Roo.bootstrap.DocumentManager} this
32382 * @param {File} file
32387 * Fire when xhr load exception
32388 * @param {Roo.bootstrap.DocumentManager} this
32389 * @param {XMLHttpRequest} xhr
32391 "exception" : true,
32393 * @event afterupload
32394 * Fire when xhr load exception
32395 * @param {Roo.bootstrap.DocumentManager} this
32396 * @param {XMLHttpRequest} xhr
32398 "afterupload" : true,
32401 * prepare the form data
32402 * @param {Roo.bootstrap.DocumentManager} this
32403 * @param {Object} formData
32408 * Fire when remove the file
32409 * @param {Roo.bootstrap.DocumentManager} this
32410 * @param {Object} file
32415 * Fire after refresh the file
32416 * @param {Roo.bootstrap.DocumentManager} this
32421 * Fire after click the image
32422 * @param {Roo.bootstrap.DocumentManager} this
32423 * @param {Object} file
32428 * Fire when upload a image and editable set to true
32429 * @param {Roo.bootstrap.DocumentManager} this
32430 * @param {Object} file
32434 * @event beforeselectfile
32435 * Fire before select file
32436 * @param {Roo.bootstrap.DocumentManager} this
32438 "beforeselectfile" : true,
32441 * Fire before process file
32442 * @param {Roo.bootstrap.DocumentManager} this
32443 * @param {Object} file
32447 * @event previewrendered
32448 * Fire when preview rendered
32449 * @param {Roo.bootstrap.DocumentManager} this
32450 * @param {Object} file
32452 "previewrendered" : true,
32455 "previewResize" : true
32460 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32469 paramName : 'imageUpload',
32470 toolTipName : 'filename',
32473 labelAlign : 'left',
32483 getAutoCreate : function()
32485 var managerWidget = {
32487 cls : 'roo-document-manager',
32491 cls : 'roo-document-manager-selector',
32496 cls : 'roo-document-manager-uploader',
32500 cls : 'roo-document-manager-upload-btn',
32501 html : '<i class="fa fa-plus"></i>'
32512 cls : 'column col-md-12',
32517 if(this.fieldLabel.length){
32522 cls : 'column col-md-12',
32523 html : this.fieldLabel
32527 cls : 'column col-md-12',
32532 if(this.labelAlign == 'left'){
32537 html : this.fieldLabel
32546 if(this.labelWidth > 12){
32547 content[0].style = "width: " + this.labelWidth + 'px';
32550 if(this.labelWidth < 13 && this.labelmd == 0){
32551 this.labelmd = this.labelWidth;
32554 if(this.labellg > 0){
32555 content[0].cls += ' col-lg-' + this.labellg;
32556 content[1].cls += ' col-lg-' + (12 - this.labellg);
32559 if(this.labelmd > 0){
32560 content[0].cls += ' col-md-' + this.labelmd;
32561 content[1].cls += ' col-md-' + (12 - this.labelmd);
32564 if(this.labelsm > 0){
32565 content[0].cls += ' col-sm-' + this.labelsm;
32566 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32569 if(this.labelxs > 0){
32570 content[0].cls += ' col-xs-' + this.labelxs;
32571 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32579 cls : 'row clearfix',
32587 initEvents : function()
32589 this.managerEl = this.el.select('.roo-document-manager', true).first();
32590 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32592 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32593 this.selectorEl.hide();
32596 this.selectorEl.attr('multiple', 'multiple');
32599 this.selectorEl.on('change', this.onFileSelected, this);
32601 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32602 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32604 this.uploader.on('click', this.onUploaderClick, this);
32606 this.renderProgressDialog();
32610 window.addEventListener("resize", function() { _this.refresh(); } );
32612 this.fireEvent('initial', this);
32615 renderProgressDialog : function()
32619 this.progressDialog = new Roo.bootstrap.Modal({
32620 cls : 'roo-document-manager-progress-dialog',
32621 allow_close : false,
32632 btnclick : function() {
32633 _this.uploadCancel();
32639 this.progressDialog.render(Roo.get(document.body));
32641 this.progress = new Roo.bootstrap.Progress({
32642 cls : 'roo-document-manager-progress',
32647 this.progress.render(this.progressDialog.getChildContainer());
32649 this.progressBar = new Roo.bootstrap.ProgressBar({
32650 cls : 'roo-document-manager-progress-bar',
32653 aria_valuemax : 12,
32657 this.progressBar.render(this.progress.getChildContainer());
32660 onUploaderClick : function(e)
32662 e.preventDefault();
32664 if(this.fireEvent('beforeselectfile', this) != false){
32665 this.selectorEl.dom.click();
32670 onFileSelected : function(e)
32672 e.preventDefault();
32674 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32678 Roo.each(this.selectorEl.dom.files, function(file){
32679 if(this.fireEvent('inspect', this, file) != false){
32680 this.files.push(file);
32690 this.selectorEl.dom.value = '';
32692 if(!this.files || !this.files.length){
32696 if(this.boxes > 0 && this.files.length > this.boxes){
32697 this.files = this.files.slice(0, this.boxes);
32700 this.uploader.show();
32702 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32703 this.uploader.hide();
32712 Roo.each(this.files, function(file){
32714 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32715 var f = this.renderPreview(file);
32720 if(file.type.indexOf('image') != -1){
32721 this.delegates.push(
32723 _this.process(file);
32724 }).createDelegate(this)
32732 _this.process(file);
32733 }).createDelegate(this)
32738 this.files = files;
32740 this.delegates = this.delegates.concat(docs);
32742 if(!this.delegates.length){
32747 this.progressBar.aria_valuemax = this.delegates.length;
32754 arrange : function()
32756 if(!this.delegates.length){
32757 this.progressDialog.hide();
32762 var delegate = this.delegates.shift();
32764 this.progressDialog.show();
32766 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32768 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32773 refresh : function()
32775 this.uploader.show();
32777 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32778 this.uploader.hide();
32781 Roo.isTouch ? this.closable(false) : this.closable(true);
32783 this.fireEvent('refresh', this);
32786 onRemove : function(e, el, o)
32788 e.preventDefault();
32790 this.fireEvent('remove', this, o);
32794 remove : function(o)
32798 Roo.each(this.files, function(file){
32799 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32808 this.files = files;
32815 Roo.each(this.files, function(file){
32820 file.target.remove();
32829 onClick : function(e, el, o)
32831 e.preventDefault();
32833 this.fireEvent('click', this, o);
32837 closable : function(closable)
32839 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32841 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32853 xhrOnLoad : function(xhr)
32855 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32859 if (xhr.readyState !== 4) {
32861 this.fireEvent('exception', this, xhr);
32865 var response = Roo.decode(xhr.responseText);
32867 if(!response.success){
32869 this.fireEvent('exception', this, xhr);
32873 var file = this.renderPreview(response.data);
32875 this.files.push(file);
32879 this.fireEvent('afterupload', this, xhr);
32883 xhrOnError : function(xhr)
32885 Roo.log('xhr on error');
32887 var response = Roo.decode(xhr.responseText);
32894 process : function(file)
32896 if(this.fireEvent('process', this, file) !== false){
32897 if(this.editable && file.type.indexOf('image') != -1){
32898 this.fireEvent('edit', this, file);
32902 this.uploadStart(file, false);
32909 uploadStart : function(file, crop)
32911 this.xhr = new XMLHttpRequest();
32913 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32918 file.xhr = this.xhr;
32920 this.managerEl.createChild({
32922 cls : 'roo-document-manager-loading',
32926 tooltip : file.name,
32927 cls : 'roo-document-manager-thumb',
32928 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32934 this.xhr.open(this.method, this.url, true);
32937 "Accept": "application/json",
32938 "Cache-Control": "no-cache",
32939 "X-Requested-With": "XMLHttpRequest"
32942 for (var headerName in headers) {
32943 var headerValue = headers[headerName];
32945 this.xhr.setRequestHeader(headerName, headerValue);
32951 this.xhr.onload = function()
32953 _this.xhrOnLoad(_this.xhr);
32956 this.xhr.onerror = function()
32958 _this.xhrOnError(_this.xhr);
32961 var formData = new FormData();
32963 formData.append('returnHTML', 'NO');
32966 formData.append('crop', crop);
32969 formData.append(this.paramName, file, file.name);
32976 if(this.fireEvent('prepare', this, formData, options) != false){
32978 if(options.manually){
32982 this.xhr.send(formData);
32986 this.uploadCancel();
32989 uploadCancel : function()
32995 this.delegates = [];
32997 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33004 renderPreview : function(file)
33006 if(typeof(file.target) != 'undefined' && file.target){
33010 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33012 var previewEl = this.managerEl.createChild({
33014 cls : 'roo-document-manager-preview',
33018 tooltip : file[this.toolTipName],
33019 cls : 'roo-document-manager-thumb',
33020 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33025 html : '<i class="fa fa-times-circle"></i>'
33030 var close = previewEl.select('button.close', true).first();
33032 close.on('click', this.onRemove, this, file);
33034 file.target = previewEl;
33036 var image = previewEl.select('img', true).first();
33040 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33042 image.on('click', this.onClick, this, file);
33044 this.fireEvent('previewrendered', this, file);
33050 onPreviewLoad : function(file, image)
33052 if(typeof(file.target) == 'undefined' || !file.target){
33056 var width = image.dom.naturalWidth || image.dom.width;
33057 var height = image.dom.naturalHeight || image.dom.height;
33059 if(!this.previewResize) {
33063 if(width > height){
33064 file.target.addClass('wide');
33068 file.target.addClass('tall');
33073 uploadFromSource : function(file, crop)
33075 this.xhr = new XMLHttpRequest();
33077 this.managerEl.createChild({
33079 cls : 'roo-document-manager-loading',
33083 tooltip : file.name,
33084 cls : 'roo-document-manager-thumb',
33085 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33091 this.xhr.open(this.method, this.url, true);
33094 "Accept": "application/json",
33095 "Cache-Control": "no-cache",
33096 "X-Requested-With": "XMLHttpRequest"
33099 for (var headerName in headers) {
33100 var headerValue = headers[headerName];
33102 this.xhr.setRequestHeader(headerName, headerValue);
33108 this.xhr.onload = function()
33110 _this.xhrOnLoad(_this.xhr);
33113 this.xhr.onerror = function()
33115 _this.xhrOnError(_this.xhr);
33118 var formData = new FormData();
33120 formData.append('returnHTML', 'NO');
33122 formData.append('crop', crop);
33124 if(typeof(file.filename) != 'undefined'){
33125 formData.append('filename', file.filename);
33128 if(typeof(file.mimetype) != 'undefined'){
33129 formData.append('mimetype', file.mimetype);
33134 if(this.fireEvent('prepare', this, formData) != false){
33135 this.xhr.send(formData);
33145 * @class Roo.bootstrap.DocumentViewer
33146 * @extends Roo.bootstrap.Component
33147 * Bootstrap DocumentViewer class
33148 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33149 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33152 * Create a new DocumentViewer
33153 * @param {Object} config The config object
33156 Roo.bootstrap.DocumentViewer = function(config){
33157 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33162 * Fire after initEvent
33163 * @param {Roo.bootstrap.DocumentViewer} this
33169 * @param {Roo.bootstrap.DocumentViewer} this
33174 * Fire after download button
33175 * @param {Roo.bootstrap.DocumentViewer} this
33180 * Fire after trash button
33181 * @param {Roo.bootstrap.DocumentViewer} this
33188 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33190 showDownload : true,
33194 getAutoCreate : function()
33198 cls : 'roo-document-viewer',
33202 cls : 'roo-document-viewer-body',
33206 cls : 'roo-document-viewer-thumb',
33210 cls : 'roo-document-viewer-image'
33218 cls : 'roo-document-viewer-footer',
33221 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33225 cls : 'btn-group roo-document-viewer-download',
33229 cls : 'btn btn-default',
33230 html : '<i class="fa fa-download"></i>'
33236 cls : 'btn-group roo-document-viewer-trash',
33240 cls : 'btn btn-default',
33241 html : '<i class="fa fa-trash"></i>'
33254 initEvents : function()
33256 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33257 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33259 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33260 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33262 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33263 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33265 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33266 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33268 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33269 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33271 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33272 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33274 this.bodyEl.on('click', this.onClick, this);
33275 this.downloadBtn.on('click', this.onDownload, this);
33276 this.trashBtn.on('click', this.onTrash, this);
33278 this.downloadBtn.hide();
33279 this.trashBtn.hide();
33281 if(this.showDownload){
33282 this.downloadBtn.show();
33285 if(this.showTrash){
33286 this.trashBtn.show();
33289 if(!this.showDownload && !this.showTrash) {
33290 this.footerEl.hide();
33295 initial : function()
33297 this.fireEvent('initial', this);
33301 onClick : function(e)
33303 e.preventDefault();
33305 this.fireEvent('click', this);
33308 onDownload : function(e)
33310 e.preventDefault();
33312 this.fireEvent('download', this);
33315 onTrash : function(e)
33317 e.preventDefault();
33319 this.fireEvent('trash', this);
33331 * @class Roo.bootstrap.NavProgressBar
33332 * @extends Roo.bootstrap.Component
33333 * Bootstrap NavProgressBar class
33336 * Create a new nav progress bar
33337 * @param {Object} config The config object
33340 Roo.bootstrap.NavProgressBar = function(config){
33341 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33343 this.bullets = this.bullets || [];
33345 // Roo.bootstrap.NavProgressBar.register(this);
33349 * Fires when the active item changes
33350 * @param {Roo.bootstrap.NavProgressBar} this
33351 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33352 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
33359 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
33364 getAutoCreate : function()
33366 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33370 cls : 'roo-navigation-bar-group',
33374 cls : 'roo-navigation-top-bar'
33378 cls : 'roo-navigation-bullets-bar',
33382 cls : 'roo-navigation-bar'
33389 cls : 'roo-navigation-bottom-bar'
33399 initEvents: function()
33404 onRender : function(ct, position)
33406 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33408 if(this.bullets.length){
33409 Roo.each(this.bullets, function(b){
33418 addItem : function(cfg)
33420 var item = new Roo.bootstrap.NavProgressItem(cfg);
33422 item.parentId = this.id;
33423 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33426 var top = new Roo.bootstrap.Element({
33428 cls : 'roo-navigation-bar-text'
33431 var bottom = new Roo.bootstrap.Element({
33433 cls : 'roo-navigation-bar-text'
33436 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33437 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33439 var topText = new Roo.bootstrap.Element({
33441 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33444 var bottomText = new Roo.bootstrap.Element({
33446 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33449 topText.onRender(top.el, null);
33450 bottomText.onRender(bottom.el, null);
33453 item.bottomEl = bottom;
33456 this.barItems.push(item);
33461 getActive : function()
33463 var active = false;
33465 Roo.each(this.barItems, function(v){
33467 if (!v.isActive()) {
33479 setActiveItem : function(item)
33483 Roo.each(this.barItems, function(v){
33484 if (v.rid == item.rid) {
33488 if (v.isActive()) {
33489 v.setActive(false);
33494 item.setActive(true);
33496 this.fireEvent('changed', this, item, prev);
33499 getBarItem: function(rid)
33503 Roo.each(this.barItems, function(e) {
33504 if (e.rid != rid) {
33515 indexOfItem : function(item)
33519 Roo.each(this.barItems, function(v, i){
33521 if (v.rid != item.rid) {
33532 setActiveNext : function()
33534 var i = this.indexOfItem(this.getActive());
33536 if (i > this.barItems.length) {
33540 this.setActiveItem(this.barItems[i+1]);
33543 setActivePrev : function()
33545 var i = this.indexOfItem(this.getActive());
33551 this.setActiveItem(this.barItems[i-1]);
33554 format : function()
33556 if(!this.barItems.length){
33560 var width = 100 / this.barItems.length;
33562 Roo.each(this.barItems, function(i){
33563 i.el.setStyle('width', width + '%');
33564 i.topEl.el.setStyle('width', width + '%');
33565 i.bottomEl.el.setStyle('width', width + '%');
33574 * Nav Progress Item
33579 * @class Roo.bootstrap.NavProgressItem
33580 * @extends Roo.bootstrap.Component
33581 * Bootstrap NavProgressItem class
33582 * @cfg {String} rid the reference id
33583 * @cfg {Boolean} active (true|false) Is item active default false
33584 * @cfg {Boolean} disabled (true|false) Is item active default false
33585 * @cfg {String} html
33586 * @cfg {String} position (top|bottom) text position default bottom
33587 * @cfg {String} icon show icon instead of number
33590 * Create a new NavProgressItem
33591 * @param {Object} config The config object
33593 Roo.bootstrap.NavProgressItem = function(config){
33594 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33599 * The raw click event for the entire grid.
33600 * @param {Roo.bootstrap.NavProgressItem} this
33601 * @param {Roo.EventObject} e
33608 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33614 position : 'bottom',
33617 getAutoCreate : function()
33619 var iconCls = 'roo-navigation-bar-item-icon';
33621 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33625 cls: 'roo-navigation-bar-item',
33635 cfg.cls += ' active';
33638 cfg.cls += ' disabled';
33644 disable : function()
33646 this.setDisabled(true);
33649 enable : function()
33651 this.setDisabled(false);
33654 initEvents: function()
33656 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33658 this.iconEl.on('click', this.onClick, this);
33661 onClick : function(e)
33663 e.preventDefault();
33669 if(this.fireEvent('click', this, e) === false){
33673 this.parent().setActiveItem(this);
33676 isActive: function ()
33678 return this.active;
33681 setActive : function(state)
33683 if(this.active == state){
33687 this.active = state;
33690 this.el.addClass('active');
33694 this.el.removeClass('active');
33699 setDisabled : function(state)
33701 if(this.disabled == state){
33705 this.disabled = state;
33708 this.el.addClass('disabled');
33712 this.el.removeClass('disabled');
33715 tooltipEl : function()
33717 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33730 * @class Roo.bootstrap.FieldLabel
33731 * @extends Roo.bootstrap.Component
33732 * Bootstrap FieldLabel class
33733 * @cfg {String} html contents of the element
33734 * @cfg {String} tag tag of the element default label
33735 * @cfg {String} cls class of the element
33736 * @cfg {String} target label target
33737 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33738 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33739 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33740 * @cfg {String} iconTooltip default "This field is required"
33741 * @cfg {String} indicatorpos (left|right) default left
33744 * Create a new FieldLabel
33745 * @param {Object} config The config object
33748 Roo.bootstrap.FieldLabel = function(config){
33749 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33754 * Fires after the field has been marked as invalid.
33755 * @param {Roo.form.FieldLabel} this
33756 * @param {String} msg The validation message
33761 * Fires after the field has been validated with no errors.
33762 * @param {Roo.form.FieldLabel} this
33768 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33775 invalidClass : 'has-warning',
33776 validClass : 'has-success',
33777 iconTooltip : 'This field is required',
33778 indicatorpos : 'left',
33780 getAutoCreate : function(){
33783 if (!this.allowBlank) {
33789 cls : 'roo-bootstrap-field-label ' + this.cls,
33794 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33795 tooltip : this.iconTooltip
33804 if(this.indicatorpos == 'right'){
33807 cls : 'roo-bootstrap-field-label ' + this.cls,
33816 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33817 tooltip : this.iconTooltip
33826 initEvents: function()
33828 Roo.bootstrap.Element.superclass.initEvents.call(this);
33830 this.indicator = this.indicatorEl();
33832 if(this.indicator){
33833 this.indicator.removeClass('visible');
33834 this.indicator.addClass('invisible');
33837 Roo.bootstrap.FieldLabel.register(this);
33840 indicatorEl : function()
33842 var indicator = this.el.select('i.roo-required-indicator',true).first();
33853 * Mark this field as valid
33855 markValid : function()
33857 if(this.indicator){
33858 this.indicator.removeClass('visible');
33859 this.indicator.addClass('invisible');
33861 if (Roo.bootstrap.version == 3) {
33862 this.el.removeClass(this.invalidClass);
33863 this.el.addClass(this.validClass);
33865 this.el.removeClass('is-invalid');
33866 this.el.addClass('is-valid');
33870 this.fireEvent('valid', this);
33874 * Mark this field as invalid
33875 * @param {String} msg The validation message
33877 markInvalid : function(msg)
33879 if(this.indicator){
33880 this.indicator.removeClass('invisible');
33881 this.indicator.addClass('visible');
33883 if (Roo.bootstrap.version == 3) {
33884 this.el.removeClass(this.validClass);
33885 this.el.addClass(this.invalidClass);
33887 this.el.removeClass('is-valid');
33888 this.el.addClass('is-invalid');
33892 this.fireEvent('invalid', this, msg);
33898 Roo.apply(Roo.bootstrap.FieldLabel, {
33903 * register a FieldLabel Group
33904 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33906 register : function(label)
33908 if(this.groups.hasOwnProperty(label.target)){
33912 this.groups[label.target] = label;
33916 * fetch a FieldLabel Group based on the target
33917 * @param {string} target
33918 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33920 get: function(target) {
33921 if (typeof(this.groups[target]) == 'undefined') {
33925 return this.groups[target] ;
33934 * page DateSplitField.
33940 * @class Roo.bootstrap.DateSplitField
33941 * @extends Roo.bootstrap.Component
33942 * Bootstrap DateSplitField class
33943 * @cfg {string} fieldLabel - the label associated
33944 * @cfg {Number} labelWidth set the width of label (0-12)
33945 * @cfg {String} labelAlign (top|left)
33946 * @cfg {Boolean} dayAllowBlank (true|false) default false
33947 * @cfg {Boolean} monthAllowBlank (true|false) default false
33948 * @cfg {Boolean} yearAllowBlank (true|false) default false
33949 * @cfg {string} dayPlaceholder
33950 * @cfg {string} monthPlaceholder
33951 * @cfg {string} yearPlaceholder
33952 * @cfg {string} dayFormat default 'd'
33953 * @cfg {string} monthFormat default 'm'
33954 * @cfg {string} yearFormat default 'Y'
33955 * @cfg {Number} labellg set the width of label (1-12)
33956 * @cfg {Number} labelmd set the width of label (1-12)
33957 * @cfg {Number} labelsm set the width of label (1-12)
33958 * @cfg {Number} labelxs set the width of label (1-12)
33962 * Create a new DateSplitField
33963 * @param {Object} config The config object
33966 Roo.bootstrap.DateSplitField = function(config){
33967 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33973 * getting the data of years
33974 * @param {Roo.bootstrap.DateSplitField} this
33975 * @param {Object} years
33980 * getting the data of days
33981 * @param {Roo.bootstrap.DateSplitField} this
33982 * @param {Object} days
33987 * Fires after the field has been marked as invalid.
33988 * @param {Roo.form.Field} this
33989 * @param {String} msg The validation message
33994 * Fires after the field has been validated with no errors.
33995 * @param {Roo.form.Field} this
34001 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
34004 labelAlign : 'top',
34006 dayAllowBlank : false,
34007 monthAllowBlank : false,
34008 yearAllowBlank : false,
34009 dayPlaceholder : '',
34010 monthPlaceholder : '',
34011 yearPlaceholder : '',
34015 isFormField : true,
34021 getAutoCreate : function()
34025 cls : 'row roo-date-split-field-group',
34030 cls : 'form-hidden-field roo-date-split-field-group-value',
34036 var labelCls = 'col-md-12';
34037 var contentCls = 'col-md-4';
34039 if(this.fieldLabel){
34043 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34047 html : this.fieldLabel
34052 if(this.labelAlign == 'left'){
34054 if(this.labelWidth > 12){
34055 label.style = "width: " + this.labelWidth + 'px';
34058 if(this.labelWidth < 13 && this.labelmd == 0){
34059 this.labelmd = this.labelWidth;
34062 if(this.labellg > 0){
34063 labelCls = ' col-lg-' + this.labellg;
34064 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34067 if(this.labelmd > 0){
34068 labelCls = ' col-md-' + this.labelmd;
34069 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34072 if(this.labelsm > 0){
34073 labelCls = ' col-sm-' + this.labelsm;
34074 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34077 if(this.labelxs > 0){
34078 labelCls = ' col-xs-' + this.labelxs;
34079 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34083 label.cls += ' ' + labelCls;
34085 cfg.cn.push(label);
34088 Roo.each(['day', 'month', 'year'], function(t){
34091 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34098 inputEl: function ()
34100 return this.el.select('.roo-date-split-field-group-value', true).first();
34103 onRender : function(ct, position)
34107 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34109 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34111 this.dayField = new Roo.bootstrap.ComboBox({
34112 allowBlank : this.dayAllowBlank,
34113 alwaysQuery : true,
34114 displayField : 'value',
34117 forceSelection : true,
34119 placeholder : this.dayPlaceholder,
34120 selectOnFocus : true,
34121 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34122 triggerAction : 'all',
34124 valueField : 'value',
34125 store : new Roo.data.SimpleStore({
34126 data : (function() {
34128 _this.fireEvent('days', _this, days);
34131 fields : [ 'value' ]
34134 select : function (_self, record, index)
34136 _this.setValue(_this.getValue());
34141 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34143 this.monthField = new Roo.bootstrap.MonthField({
34144 after : '<i class=\"fa fa-calendar\"></i>',
34145 allowBlank : this.monthAllowBlank,
34146 placeholder : this.monthPlaceholder,
34149 render : function (_self)
34151 this.el.select('span.input-group-addon', true).first().on('click', function(e){
34152 e.preventDefault();
34156 select : function (_self, oldvalue, newvalue)
34158 _this.setValue(_this.getValue());
34163 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34165 this.yearField = new Roo.bootstrap.ComboBox({
34166 allowBlank : this.yearAllowBlank,
34167 alwaysQuery : true,
34168 displayField : 'value',
34171 forceSelection : true,
34173 placeholder : this.yearPlaceholder,
34174 selectOnFocus : true,
34175 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34176 triggerAction : 'all',
34178 valueField : 'value',
34179 store : new Roo.data.SimpleStore({
34180 data : (function() {
34182 _this.fireEvent('years', _this, years);
34185 fields : [ 'value' ]
34188 select : function (_self, record, index)
34190 _this.setValue(_this.getValue());
34195 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34198 setValue : function(v, format)
34200 this.inputEl.dom.value = v;
34202 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34204 var d = Date.parseDate(v, f);
34211 this.setDay(d.format(this.dayFormat));
34212 this.setMonth(d.format(this.monthFormat));
34213 this.setYear(d.format(this.yearFormat));
34220 setDay : function(v)
34222 this.dayField.setValue(v);
34223 this.inputEl.dom.value = this.getValue();
34228 setMonth : function(v)
34230 this.monthField.setValue(v, true);
34231 this.inputEl.dom.value = this.getValue();
34236 setYear : function(v)
34238 this.yearField.setValue(v);
34239 this.inputEl.dom.value = this.getValue();
34244 getDay : function()
34246 return this.dayField.getValue();
34249 getMonth : function()
34251 return this.monthField.getValue();
34254 getYear : function()
34256 return this.yearField.getValue();
34259 getValue : function()
34261 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34263 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34273 this.inputEl.dom.value = '';
34278 validate : function()
34280 var d = this.dayField.validate();
34281 var m = this.monthField.validate();
34282 var y = this.yearField.validate();
34287 (!this.dayAllowBlank && !d) ||
34288 (!this.monthAllowBlank && !m) ||
34289 (!this.yearAllowBlank && !y)
34294 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34303 this.markInvalid();
34308 markValid : function()
34311 var label = this.el.select('label', true).first();
34312 var icon = this.el.select('i.fa-star', true).first();
34318 this.fireEvent('valid', this);
34322 * Mark this field as invalid
34323 * @param {String} msg The validation message
34325 markInvalid : function(msg)
34328 var label = this.el.select('label', true).first();
34329 var icon = this.el.select('i.fa-star', true).first();
34331 if(label && !icon){
34332 this.el.select('.roo-date-split-field-label', true).createChild({
34334 cls : 'text-danger fa fa-lg fa-star',
34335 tooltip : 'This field is required',
34336 style : 'margin-right:5px;'
34340 this.fireEvent('invalid', this, msg);
34343 clearInvalid : function()
34345 var label = this.el.select('label', true).first();
34346 var icon = this.el.select('i.fa-star', true).first();
34352 this.fireEvent('valid', this);
34355 getName: function()
34365 * http://masonry.desandro.com
34367 * The idea is to render all the bricks based on vertical width...
34369 * The original code extends 'outlayer' - we might need to use that....
34375 * @class Roo.bootstrap.LayoutMasonry
34376 * @extends Roo.bootstrap.Component
34377 * Bootstrap Layout Masonry class
34380 * Create a new Element
34381 * @param {Object} config The config object
34384 Roo.bootstrap.LayoutMasonry = function(config){
34386 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34390 Roo.bootstrap.LayoutMasonry.register(this);
34396 * Fire after layout the items
34397 * @param {Roo.bootstrap.LayoutMasonry} this
34398 * @param {Roo.EventObject} e
34405 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34408 * @cfg {Boolean} isLayoutInstant = no animation?
34410 isLayoutInstant : false, // needed?
34413 * @cfg {Number} boxWidth width of the columns
34418 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34423 * @cfg {Number} padWidth padding below box..
34428 * @cfg {Number} gutter gutter width..
34433 * @cfg {Number} maxCols maximum number of columns
34439 * @cfg {Boolean} isAutoInitial defalut true
34441 isAutoInitial : true,
34446 * @cfg {Boolean} isHorizontal defalut false
34448 isHorizontal : false,
34450 currentSize : null,
34456 bricks: null, //CompositeElement
34460 _isLayoutInited : false,
34462 // isAlternative : false, // only use for vertical layout...
34465 * @cfg {Number} alternativePadWidth padding below box..
34467 alternativePadWidth : 50,
34469 selectedBrick : [],
34471 getAutoCreate : function(){
34473 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34477 cls: 'blog-masonary-wrapper ' + this.cls,
34479 cls : 'mas-boxes masonary'
34486 getChildContainer: function( )
34488 if (this.boxesEl) {
34489 return this.boxesEl;
34492 this.boxesEl = this.el.select('.mas-boxes').first();
34494 return this.boxesEl;
34498 initEvents : function()
34502 if(this.isAutoInitial){
34503 Roo.log('hook children rendered');
34504 this.on('childrenrendered', function() {
34505 Roo.log('children rendered');
34511 initial : function()
34513 this.selectedBrick = [];
34515 this.currentSize = this.el.getBox(true);
34517 Roo.EventManager.onWindowResize(this.resize, this);
34519 if(!this.isAutoInitial){
34527 //this.layout.defer(500,this);
34531 resize : function()
34533 var cs = this.el.getBox(true);
34536 this.currentSize.width == cs.width &&
34537 this.currentSize.x == cs.x &&
34538 this.currentSize.height == cs.height &&
34539 this.currentSize.y == cs.y
34541 Roo.log("no change in with or X or Y");
34545 this.currentSize = cs;
34551 layout : function()
34553 this._resetLayout();
34555 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34557 this.layoutItems( isInstant );
34559 this._isLayoutInited = true;
34561 this.fireEvent('layout', this);
34565 _resetLayout : function()
34567 if(this.isHorizontal){
34568 this.horizontalMeasureColumns();
34572 this.verticalMeasureColumns();
34576 verticalMeasureColumns : function()
34578 this.getContainerWidth();
34580 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34581 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34585 var boxWidth = this.boxWidth + this.padWidth;
34587 if(this.containerWidth < this.boxWidth){
34588 boxWidth = this.containerWidth
34591 var containerWidth = this.containerWidth;
34593 var cols = Math.floor(containerWidth / boxWidth);
34595 this.cols = Math.max( cols, 1 );
34597 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34599 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34601 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34603 this.colWidth = boxWidth + avail - this.padWidth;
34605 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34606 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34609 horizontalMeasureColumns : function()
34611 this.getContainerWidth();
34613 var boxWidth = this.boxWidth;
34615 if(this.containerWidth < boxWidth){
34616 boxWidth = this.containerWidth;
34619 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34621 this.el.setHeight(boxWidth);
34625 getContainerWidth : function()
34627 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34630 layoutItems : function( isInstant )
34632 Roo.log(this.bricks);
34634 var items = Roo.apply([], this.bricks);
34636 if(this.isHorizontal){
34637 this._horizontalLayoutItems( items , isInstant );
34641 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34642 // this._verticalAlternativeLayoutItems( items , isInstant );
34646 this._verticalLayoutItems( items , isInstant );
34650 _verticalLayoutItems : function ( items , isInstant)
34652 if ( !items || !items.length ) {
34657 ['xs', 'xs', 'xs', 'tall'],
34658 ['xs', 'xs', 'tall'],
34659 ['xs', 'xs', 'sm'],
34660 ['xs', 'xs', 'xs'],
34666 ['sm', 'xs', 'xs'],
34670 ['tall', 'xs', 'xs', 'xs'],
34671 ['tall', 'xs', 'xs'],
34683 Roo.each(items, function(item, k){
34685 switch (item.size) {
34686 // these layouts take up a full box,
34697 boxes.push([item]);
34720 var filterPattern = function(box, length)
34728 var pattern = box.slice(0, length);
34732 Roo.each(pattern, function(i){
34733 format.push(i.size);
34736 Roo.each(standard, function(s){
34738 if(String(s) != String(format)){
34747 if(!match && length == 1){
34752 filterPattern(box, length - 1);
34756 queue.push(pattern);
34758 box = box.slice(length, box.length);
34760 filterPattern(box, 4);
34766 Roo.each(boxes, function(box, k){
34772 if(box.length == 1){
34777 filterPattern(box, 4);
34781 this._processVerticalLayoutQueue( queue, isInstant );
34785 // _verticalAlternativeLayoutItems : function( items , isInstant )
34787 // if ( !items || !items.length ) {
34791 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34795 _horizontalLayoutItems : function ( items , isInstant)
34797 if ( !items || !items.length || items.length < 3) {
34803 var eItems = items.slice(0, 3);
34805 items = items.slice(3, items.length);
34808 ['xs', 'xs', 'xs', 'wide'],
34809 ['xs', 'xs', 'wide'],
34810 ['xs', 'xs', 'sm'],
34811 ['xs', 'xs', 'xs'],
34817 ['sm', 'xs', 'xs'],
34821 ['wide', 'xs', 'xs', 'xs'],
34822 ['wide', 'xs', 'xs'],
34835 Roo.each(items, function(item, k){
34837 switch (item.size) {
34848 boxes.push([item]);
34872 var filterPattern = function(box, length)
34880 var pattern = box.slice(0, length);
34884 Roo.each(pattern, function(i){
34885 format.push(i.size);
34888 Roo.each(standard, function(s){
34890 if(String(s) != String(format)){
34899 if(!match && length == 1){
34904 filterPattern(box, length - 1);
34908 queue.push(pattern);
34910 box = box.slice(length, box.length);
34912 filterPattern(box, 4);
34918 Roo.each(boxes, function(box, k){
34924 if(box.length == 1){
34929 filterPattern(box, 4);
34936 var pos = this.el.getBox(true);
34940 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34942 var hit_end = false;
34944 Roo.each(queue, function(box){
34948 Roo.each(box, function(b){
34950 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34960 Roo.each(box, function(b){
34962 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34965 mx = Math.max(mx, b.x);
34969 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34973 Roo.each(box, function(b){
34975 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34989 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34992 /** Sets position of item in DOM
34993 * @param {Element} item
34994 * @param {Number} x - horizontal position
34995 * @param {Number} y - vertical position
34996 * @param {Boolean} isInstant - disables transitions
34998 _processVerticalLayoutQueue : function( queue, isInstant )
35000 var pos = this.el.getBox(true);
35005 for (var i = 0; i < this.cols; i++){
35009 Roo.each(queue, function(box, k){
35011 var col = k % this.cols;
35013 Roo.each(box, function(b,kk){
35015 b.el.position('absolute');
35017 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35018 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35020 if(b.size == 'md-left' || b.size == 'md-right'){
35021 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35022 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35025 b.el.setWidth(width);
35026 b.el.setHeight(height);
35028 b.el.select('iframe',true).setSize(width,height);
35032 for (var i = 0; i < this.cols; i++){
35034 if(maxY[i] < maxY[col]){
35039 col = Math.min(col, i);
35043 x = pos.x + col * (this.colWidth + this.padWidth);
35047 var positions = [];
35049 switch (box.length){
35051 positions = this.getVerticalOneBoxColPositions(x, y, box);
35054 positions = this.getVerticalTwoBoxColPositions(x, y, box);
35057 positions = this.getVerticalThreeBoxColPositions(x, y, box);
35060 positions = this.getVerticalFourBoxColPositions(x, y, box);
35066 Roo.each(box, function(b,kk){
35068 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35070 var sz = b.el.getSize();
35072 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35080 for (var i = 0; i < this.cols; i++){
35081 mY = Math.max(mY, maxY[i]);
35084 this.el.setHeight(mY - pos.y);
35088 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35090 // var pos = this.el.getBox(true);
35093 // var maxX = pos.right;
35095 // var maxHeight = 0;
35097 // Roo.each(items, function(item, k){
35101 // item.el.position('absolute');
35103 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35105 // item.el.setWidth(width);
35107 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35109 // item.el.setHeight(height);
35112 // item.el.setXY([x, y], isInstant ? false : true);
35114 // item.el.setXY([maxX - width, y], isInstant ? false : true);
35117 // y = y + height + this.alternativePadWidth;
35119 // maxHeight = maxHeight + height + this.alternativePadWidth;
35123 // this.el.setHeight(maxHeight);
35127 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35129 var pos = this.el.getBox(true);
35134 var maxX = pos.right;
35136 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35138 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35140 Roo.each(queue, function(box, k){
35142 Roo.each(box, function(b, kk){
35144 b.el.position('absolute');
35146 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35147 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35149 if(b.size == 'md-left' || b.size == 'md-right'){
35150 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35151 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35154 b.el.setWidth(width);
35155 b.el.setHeight(height);
35163 var positions = [];
35165 switch (box.length){
35167 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35170 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35173 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35176 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35182 Roo.each(box, function(b,kk){
35184 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35186 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35194 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35196 Roo.each(eItems, function(b,k){
35198 b.size = (k == 0) ? 'sm' : 'xs';
35199 b.x = (k == 0) ? 2 : 1;
35200 b.y = (k == 0) ? 2 : 1;
35202 b.el.position('absolute');
35204 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35206 b.el.setWidth(width);
35208 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35210 b.el.setHeight(height);
35214 var positions = [];
35217 x : maxX - this.unitWidth * 2 - this.gutter,
35222 x : maxX - this.unitWidth,
35223 y : minY + (this.unitWidth + this.gutter) * 2
35227 x : maxX - this.unitWidth * 3 - this.gutter * 2,
35231 Roo.each(eItems, function(b,k){
35233 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35239 getVerticalOneBoxColPositions : function(x, y, box)
35243 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35245 if(box[0].size == 'md-left'){
35249 if(box[0].size == 'md-right'){
35254 x : x + (this.unitWidth + this.gutter) * rand,
35261 getVerticalTwoBoxColPositions : function(x, y, box)
35265 if(box[0].size == 'xs'){
35269 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35273 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35287 x : x + (this.unitWidth + this.gutter) * 2,
35288 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35295 getVerticalThreeBoxColPositions : function(x, y, box)
35299 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35307 x : x + (this.unitWidth + this.gutter) * 1,
35312 x : x + (this.unitWidth + this.gutter) * 2,
35320 if(box[0].size == 'xs' && box[1].size == 'xs'){
35329 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35333 x : x + (this.unitWidth + this.gutter) * 1,
35347 x : x + (this.unitWidth + this.gutter) * 2,
35352 x : x + (this.unitWidth + this.gutter) * 2,
35353 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35360 getVerticalFourBoxColPositions : function(x, y, box)
35364 if(box[0].size == 'xs'){
35373 y : y + (this.unitHeight + this.gutter) * 1
35378 y : y + (this.unitHeight + this.gutter) * 2
35382 x : x + (this.unitWidth + this.gutter) * 1,
35396 x : x + (this.unitWidth + this.gutter) * 2,
35401 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35402 y : y + (this.unitHeight + this.gutter) * 1
35406 x : x + (this.unitWidth + this.gutter) * 2,
35407 y : y + (this.unitWidth + this.gutter) * 2
35414 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35418 if(box[0].size == 'md-left'){
35420 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35427 if(box[0].size == 'md-right'){
35429 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35430 y : minY + (this.unitWidth + this.gutter) * 1
35436 var rand = Math.floor(Math.random() * (4 - box[0].y));
35439 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35440 y : minY + (this.unitWidth + this.gutter) * rand
35447 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35451 if(box[0].size == 'xs'){
35454 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35459 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35460 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35468 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35473 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35474 y : minY + (this.unitWidth + this.gutter) * 2
35481 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35485 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35488 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35493 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35494 y : minY + (this.unitWidth + this.gutter) * 1
35498 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35499 y : minY + (this.unitWidth + this.gutter) * 2
35506 if(box[0].size == 'xs' && box[1].size == 'xs'){
35509 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35514 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35519 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35520 y : minY + (this.unitWidth + this.gutter) * 1
35528 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35533 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35534 y : minY + (this.unitWidth + this.gutter) * 2
35538 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35539 y : minY + (this.unitWidth + this.gutter) * 2
35546 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35550 if(box[0].size == 'xs'){
35553 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35558 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35563 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),
35568 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35569 y : minY + (this.unitWidth + this.gutter) * 1
35577 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35582 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35583 y : minY + (this.unitWidth + this.gutter) * 2
35587 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35588 y : minY + (this.unitWidth + this.gutter) * 2
35592 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),
35593 y : minY + (this.unitWidth + this.gutter) * 2
35601 * remove a Masonry Brick
35602 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35604 removeBrick : function(brick_id)
35610 for (var i = 0; i<this.bricks.length; i++) {
35611 if (this.bricks[i].id == brick_id) {
35612 this.bricks.splice(i,1);
35613 this.el.dom.removeChild(Roo.get(brick_id).dom);
35620 * adds a Masonry Brick
35621 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35623 addBrick : function(cfg)
35625 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35626 //this.register(cn);
35627 cn.parentId = this.id;
35628 cn.render(this.el);
35633 * register a Masonry Brick
35634 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35637 register : function(brick)
35639 this.bricks.push(brick);
35640 brick.masonryId = this.id;
35644 * clear all the Masonry Brick
35646 clearAll : function()
35649 //this.getChildContainer().dom.innerHTML = "";
35650 this.el.dom.innerHTML = '';
35653 getSelected : function()
35655 if (!this.selectedBrick) {
35659 return this.selectedBrick;
35663 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35667 * register a Masonry Layout
35668 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35671 register : function(layout)
35673 this.groups[layout.id] = layout;
35676 * fetch a Masonry Layout based on the masonry layout ID
35677 * @param {string} the masonry layout to add
35678 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35681 get: function(layout_id) {
35682 if (typeof(this.groups[layout_id]) == 'undefined') {
35685 return this.groups[layout_id] ;
35697 * http://masonry.desandro.com
35699 * The idea is to render all the bricks based on vertical width...
35701 * The original code extends 'outlayer' - we might need to use that....
35707 * @class Roo.bootstrap.LayoutMasonryAuto
35708 * @extends Roo.bootstrap.Component
35709 * Bootstrap Layout Masonry class
35712 * Create a new Element
35713 * @param {Object} config The config object
35716 Roo.bootstrap.LayoutMasonryAuto = function(config){
35717 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35720 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35723 * @cfg {Boolean} isFitWidth - resize the width..
35725 isFitWidth : false, // options..
35727 * @cfg {Boolean} isOriginLeft = left align?
35729 isOriginLeft : true,
35731 * @cfg {Boolean} isOriginTop = top align?
35733 isOriginTop : false,
35735 * @cfg {Boolean} isLayoutInstant = no animation?
35737 isLayoutInstant : false, // needed?
35739 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35741 isResizingContainer : true,
35743 * @cfg {Number} columnWidth width of the columns
35749 * @cfg {Number} maxCols maximum number of columns
35754 * @cfg {Number} padHeight padding below box..
35760 * @cfg {Boolean} isAutoInitial defalut true
35763 isAutoInitial : true,
35769 initialColumnWidth : 0,
35770 currentSize : null,
35772 colYs : null, // array.
35779 bricks: null, //CompositeElement
35780 cols : 0, // array?
35781 // element : null, // wrapped now this.el
35782 _isLayoutInited : null,
35785 getAutoCreate : function(){
35789 cls: 'blog-masonary-wrapper ' + this.cls,
35791 cls : 'mas-boxes masonary'
35798 getChildContainer: function( )
35800 if (this.boxesEl) {
35801 return this.boxesEl;
35804 this.boxesEl = this.el.select('.mas-boxes').first();
35806 return this.boxesEl;
35810 initEvents : function()
35814 if(this.isAutoInitial){
35815 Roo.log('hook children rendered');
35816 this.on('childrenrendered', function() {
35817 Roo.log('children rendered');
35824 initial : function()
35826 this.reloadItems();
35828 this.currentSize = this.el.getBox(true);
35830 /// was window resize... - let's see if this works..
35831 Roo.EventManager.onWindowResize(this.resize, this);
35833 if(!this.isAutoInitial){
35838 this.layout.defer(500,this);
35841 reloadItems: function()
35843 this.bricks = this.el.select('.masonry-brick', true);
35845 this.bricks.each(function(b) {
35846 //Roo.log(b.getSize());
35847 if (!b.attr('originalwidth')) {
35848 b.attr('originalwidth', b.getSize().width);
35853 Roo.log(this.bricks.elements.length);
35856 resize : function()
35859 var cs = this.el.getBox(true);
35861 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35862 Roo.log("no change in with or X");
35865 this.currentSize = cs;
35869 layout : function()
35872 this._resetLayout();
35873 //this._manageStamps();
35875 // don't animate first layout
35876 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35877 this.layoutItems( isInstant );
35879 // flag for initalized
35880 this._isLayoutInited = true;
35883 layoutItems : function( isInstant )
35885 //var items = this._getItemsForLayout( this.items );
35886 // original code supports filtering layout items.. we just ignore it..
35888 this._layoutItems( this.bricks , isInstant );
35890 this._postLayout();
35892 _layoutItems : function ( items , isInstant)
35894 //this.fireEvent( 'layout', this, items );
35897 if ( !items || !items.elements.length ) {
35898 // no items, emit event with empty array
35903 items.each(function(item) {
35904 Roo.log("layout item");
35906 // get x/y object from method
35907 var position = this._getItemLayoutPosition( item );
35909 position.item = item;
35910 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35911 queue.push( position );
35914 this._processLayoutQueue( queue );
35916 /** Sets position of item in DOM
35917 * @param {Element} item
35918 * @param {Number} x - horizontal position
35919 * @param {Number} y - vertical position
35920 * @param {Boolean} isInstant - disables transitions
35922 _processLayoutQueue : function( queue )
35924 for ( var i=0, len = queue.length; i < len; i++ ) {
35925 var obj = queue[i];
35926 obj.item.position('absolute');
35927 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35933 * Any logic you want to do after each layout,
35934 * i.e. size the container
35936 _postLayout : function()
35938 this.resizeContainer();
35941 resizeContainer : function()
35943 if ( !this.isResizingContainer ) {
35946 var size = this._getContainerSize();
35948 this.el.setSize(size.width,size.height);
35949 this.boxesEl.setSize(size.width,size.height);
35955 _resetLayout : function()
35957 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35958 this.colWidth = this.el.getWidth();
35959 //this.gutter = this.el.getWidth();
35961 this.measureColumns();
35967 this.colYs.push( 0 );
35973 measureColumns : function()
35975 this.getContainerWidth();
35976 // if columnWidth is 0, default to outerWidth of first item
35977 if ( !this.columnWidth ) {
35978 var firstItem = this.bricks.first();
35979 Roo.log(firstItem);
35980 this.columnWidth = this.containerWidth;
35981 if (firstItem && firstItem.attr('originalwidth') ) {
35982 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35984 // columnWidth fall back to item of first element
35985 Roo.log("set column width?");
35986 this.initialColumnWidth = this.columnWidth ;
35988 // if first elem has no width, default to size of container
35993 if (this.initialColumnWidth) {
35994 this.columnWidth = this.initialColumnWidth;
35999 // column width is fixed at the top - however if container width get's smaller we should
36002 // this bit calcs how man columns..
36004 var columnWidth = this.columnWidth += this.gutter;
36006 // calculate columns
36007 var containerWidth = this.containerWidth + this.gutter;
36009 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
36010 // fix rounding errors, typically with gutters
36011 var excess = columnWidth - containerWidth % columnWidth;
36014 // if overshoot is less than a pixel, round up, otherwise floor it
36015 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
36016 cols = Math[ mathMethod ]( cols );
36017 this.cols = Math.max( cols, 1 );
36018 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36020 // padding positioning..
36021 var totalColWidth = this.cols * this.columnWidth;
36022 var padavail = this.containerWidth - totalColWidth;
36023 // so for 2 columns - we need 3 'pads'
36025 var padNeeded = (1+this.cols) * this.padWidth;
36027 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36029 this.columnWidth += padExtra
36030 //this.padWidth = Math.floor(padavail / ( this.cols));
36032 // adjust colum width so that padding is fixed??
36034 // we have 3 columns ... total = width * 3
36035 // we have X left over... that should be used by
36037 //if (this.expandC) {
36045 getContainerWidth : function()
36047 /* // container is parent if fit width
36048 var container = this.isFitWidth ? this.element.parentNode : this.element;
36049 // check that this.size and size are there
36050 // IE8 triggers resize on body size change, so they might not be
36052 var size = getSize( container ); //FIXME
36053 this.containerWidth = size && size.innerWidth; //FIXME
36056 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
36060 _getItemLayoutPosition : function( item ) // what is item?
36062 // we resize the item to our columnWidth..
36064 item.setWidth(this.columnWidth);
36065 item.autoBoxAdjust = false;
36067 var sz = item.getSize();
36069 // how many columns does this brick span
36070 var remainder = this.containerWidth % this.columnWidth;
36072 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36073 // round if off by 1 pixel, otherwise use ceil
36074 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
36075 colSpan = Math.min( colSpan, this.cols );
36077 // normally this should be '1' as we dont' currently allow multi width columns..
36079 var colGroup = this._getColGroup( colSpan );
36080 // get the minimum Y value from the columns
36081 var minimumY = Math.min.apply( Math, colGroup );
36082 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36084 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
36086 // position the brick
36088 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36089 y: this.currentSize.y + minimumY + this.padHeight
36093 // apply setHeight to necessary columns
36094 var setHeight = minimumY + sz.height + this.padHeight;
36095 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36097 var setSpan = this.cols + 1 - colGroup.length;
36098 for ( var i = 0; i < setSpan; i++ ) {
36099 this.colYs[ shortColIndex + i ] = setHeight ;
36106 * @param {Number} colSpan - number of columns the element spans
36107 * @returns {Array} colGroup
36109 _getColGroup : function( colSpan )
36111 if ( colSpan < 2 ) {
36112 // if brick spans only one column, use all the column Ys
36117 // how many different places could this brick fit horizontally
36118 var groupCount = this.cols + 1 - colSpan;
36119 // for each group potential horizontal position
36120 for ( var i = 0; i < groupCount; i++ ) {
36121 // make an array of colY values for that one group
36122 var groupColYs = this.colYs.slice( i, i + colSpan );
36123 // and get the max value of the array
36124 colGroup[i] = Math.max.apply( Math, groupColYs );
36129 _manageStamp : function( stamp )
36131 var stampSize = stamp.getSize();
36132 var offset = stamp.getBox();
36133 // get the columns that this stamp affects
36134 var firstX = this.isOriginLeft ? offset.x : offset.right;
36135 var lastX = firstX + stampSize.width;
36136 var firstCol = Math.floor( firstX / this.columnWidth );
36137 firstCol = Math.max( 0, firstCol );
36139 var lastCol = Math.floor( lastX / this.columnWidth );
36140 // lastCol should not go over if multiple of columnWidth #425
36141 lastCol -= lastX % this.columnWidth ? 0 : 1;
36142 lastCol = Math.min( this.cols - 1, lastCol );
36144 // set colYs to bottom of the stamp
36145 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36148 for ( var i = firstCol; i <= lastCol; i++ ) {
36149 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36154 _getContainerSize : function()
36156 this.maxY = Math.max.apply( Math, this.colYs );
36161 if ( this.isFitWidth ) {
36162 size.width = this._getContainerFitWidth();
36168 _getContainerFitWidth : function()
36170 var unusedCols = 0;
36171 // count unused columns
36174 if ( this.colYs[i] !== 0 ) {
36179 // fit container to columns that have been used
36180 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36183 needsResizeLayout : function()
36185 var previousWidth = this.containerWidth;
36186 this.getContainerWidth();
36187 return previousWidth !== this.containerWidth;
36202 * @class Roo.bootstrap.MasonryBrick
36203 * @extends Roo.bootstrap.Component
36204 * Bootstrap MasonryBrick class
36207 * Create a new MasonryBrick
36208 * @param {Object} config The config object
36211 Roo.bootstrap.MasonryBrick = function(config){
36213 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36215 Roo.bootstrap.MasonryBrick.register(this);
36221 * When a MasonryBrick is clcik
36222 * @param {Roo.bootstrap.MasonryBrick} this
36223 * @param {Roo.EventObject} e
36229 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
36232 * @cfg {String} title
36236 * @cfg {String} html
36240 * @cfg {String} bgimage
36244 * @cfg {String} videourl
36248 * @cfg {String} cls
36252 * @cfg {String} href
36256 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36261 * @cfg {String} placetitle (center|bottom)
36266 * @cfg {Boolean} isFitContainer defalut true
36268 isFitContainer : true,
36271 * @cfg {Boolean} preventDefault defalut false
36273 preventDefault : false,
36276 * @cfg {Boolean} inverse defalut false
36278 maskInverse : false,
36280 getAutoCreate : function()
36282 if(!this.isFitContainer){
36283 return this.getSplitAutoCreate();
36286 var cls = 'masonry-brick masonry-brick-full';
36288 if(this.href.length){
36289 cls += ' masonry-brick-link';
36292 if(this.bgimage.length){
36293 cls += ' masonry-brick-image';
36296 if(this.maskInverse){
36297 cls += ' mask-inverse';
36300 if(!this.html.length && !this.maskInverse && !this.videourl.length){
36301 cls += ' enable-mask';
36305 cls += ' masonry-' + this.size + '-brick';
36308 if(this.placetitle.length){
36310 switch (this.placetitle) {
36312 cls += ' masonry-center-title';
36315 cls += ' masonry-bottom-title';
36322 if(!this.html.length && !this.bgimage.length){
36323 cls += ' masonry-center-title';
36326 if(!this.html.length && this.bgimage.length){
36327 cls += ' masonry-bottom-title';
36332 cls += ' ' + this.cls;
36336 tag: (this.href.length) ? 'a' : 'div',
36341 cls: 'masonry-brick-mask'
36345 cls: 'masonry-brick-paragraph',
36351 if(this.href.length){
36352 cfg.href = this.href;
36355 var cn = cfg.cn[1].cn;
36357 if(this.title.length){
36360 cls: 'masonry-brick-title',
36365 if(this.html.length){
36368 cls: 'masonry-brick-text',
36373 if (!this.title.length && !this.html.length) {
36374 cfg.cn[1].cls += ' hide';
36377 if(this.bgimage.length){
36380 cls: 'masonry-brick-image-view',
36385 if(this.videourl.length){
36386 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36387 // youtube support only?
36390 cls: 'masonry-brick-image-view',
36393 allowfullscreen : true
36401 getSplitAutoCreate : function()
36403 var cls = 'masonry-brick masonry-brick-split';
36405 if(this.href.length){
36406 cls += ' masonry-brick-link';
36409 if(this.bgimage.length){
36410 cls += ' masonry-brick-image';
36414 cls += ' masonry-' + this.size + '-brick';
36417 switch (this.placetitle) {
36419 cls += ' masonry-center-title';
36422 cls += ' masonry-bottom-title';
36425 if(!this.bgimage.length){
36426 cls += ' masonry-center-title';
36429 if(this.bgimage.length){
36430 cls += ' masonry-bottom-title';
36436 cls += ' ' + this.cls;
36440 tag: (this.href.length) ? 'a' : 'div',
36445 cls: 'masonry-brick-split-head',
36449 cls: 'masonry-brick-paragraph',
36456 cls: 'masonry-brick-split-body',
36462 if(this.href.length){
36463 cfg.href = this.href;
36466 if(this.title.length){
36467 cfg.cn[0].cn[0].cn.push({
36469 cls: 'masonry-brick-title',
36474 if(this.html.length){
36475 cfg.cn[1].cn.push({
36477 cls: 'masonry-brick-text',
36482 if(this.bgimage.length){
36483 cfg.cn[0].cn.push({
36485 cls: 'masonry-brick-image-view',
36490 if(this.videourl.length){
36491 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36492 // youtube support only?
36493 cfg.cn[0].cn.cn.push({
36495 cls: 'masonry-brick-image-view',
36498 allowfullscreen : true
36505 initEvents: function()
36507 switch (this.size) {
36540 this.el.on('touchstart', this.onTouchStart, this);
36541 this.el.on('touchmove', this.onTouchMove, this);
36542 this.el.on('touchend', this.onTouchEnd, this);
36543 this.el.on('contextmenu', this.onContextMenu, this);
36545 this.el.on('mouseenter' ,this.enter, this);
36546 this.el.on('mouseleave', this.leave, this);
36547 this.el.on('click', this.onClick, this);
36550 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36551 this.parent().bricks.push(this);
36556 onClick: function(e, el)
36558 var time = this.endTimer - this.startTimer;
36559 // Roo.log(e.preventDefault());
36562 e.preventDefault();
36567 if(!this.preventDefault){
36571 e.preventDefault();
36573 if (this.activeClass != '') {
36574 this.selectBrick();
36577 this.fireEvent('click', this, e);
36580 enter: function(e, el)
36582 e.preventDefault();
36584 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36588 if(this.bgimage.length && this.html.length){
36589 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36593 leave: function(e, el)
36595 e.preventDefault();
36597 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36601 if(this.bgimage.length && this.html.length){
36602 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36606 onTouchStart: function(e, el)
36608 // e.preventDefault();
36610 this.touchmoved = false;
36612 if(!this.isFitContainer){
36616 if(!this.bgimage.length || !this.html.length){
36620 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36622 this.timer = new Date().getTime();
36626 onTouchMove: function(e, el)
36628 this.touchmoved = true;
36631 onContextMenu : function(e,el)
36633 e.preventDefault();
36634 e.stopPropagation();
36638 onTouchEnd: function(e, el)
36640 // e.preventDefault();
36642 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36649 if(!this.bgimage.length || !this.html.length){
36651 if(this.href.length){
36652 window.location.href = this.href;
36658 if(!this.isFitContainer){
36662 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36664 window.location.href = this.href;
36667 //selection on single brick only
36668 selectBrick : function() {
36670 if (!this.parentId) {
36674 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36675 var index = m.selectedBrick.indexOf(this.id);
36678 m.selectedBrick.splice(index,1);
36679 this.el.removeClass(this.activeClass);
36683 for(var i = 0; i < m.selectedBrick.length; i++) {
36684 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36685 b.el.removeClass(b.activeClass);
36688 m.selectedBrick = [];
36690 m.selectedBrick.push(this.id);
36691 this.el.addClass(this.activeClass);
36695 isSelected : function(){
36696 return this.el.hasClass(this.activeClass);
36701 Roo.apply(Roo.bootstrap.MasonryBrick, {
36704 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36706 * register a Masonry Brick
36707 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36710 register : function(brick)
36712 //this.groups[brick.id] = brick;
36713 this.groups.add(brick.id, brick);
36716 * fetch a masonry brick based on the masonry brick ID
36717 * @param {string} the masonry brick to add
36718 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36721 get: function(brick_id)
36723 // if (typeof(this.groups[brick_id]) == 'undefined') {
36726 // return this.groups[brick_id] ;
36728 if(this.groups.key(brick_id)) {
36729 return this.groups.key(brick_id);
36747 * @class Roo.bootstrap.Brick
36748 * @extends Roo.bootstrap.Component
36749 * Bootstrap Brick class
36752 * Create a new Brick
36753 * @param {Object} config The config object
36756 Roo.bootstrap.Brick = function(config){
36757 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36763 * When a Brick is click
36764 * @param {Roo.bootstrap.Brick} this
36765 * @param {Roo.EventObject} e
36771 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36774 * @cfg {String} title
36778 * @cfg {String} html
36782 * @cfg {String} bgimage
36786 * @cfg {String} cls
36790 * @cfg {String} href
36794 * @cfg {String} video
36798 * @cfg {Boolean} square
36802 getAutoCreate : function()
36804 var cls = 'roo-brick';
36806 if(this.href.length){
36807 cls += ' roo-brick-link';
36810 if(this.bgimage.length){
36811 cls += ' roo-brick-image';
36814 if(!this.html.length && !this.bgimage.length){
36815 cls += ' roo-brick-center-title';
36818 if(!this.html.length && this.bgimage.length){
36819 cls += ' roo-brick-bottom-title';
36823 cls += ' ' + this.cls;
36827 tag: (this.href.length) ? 'a' : 'div',
36832 cls: 'roo-brick-paragraph',
36838 if(this.href.length){
36839 cfg.href = this.href;
36842 var cn = cfg.cn[0].cn;
36844 if(this.title.length){
36847 cls: 'roo-brick-title',
36852 if(this.html.length){
36855 cls: 'roo-brick-text',
36862 if(this.bgimage.length){
36865 cls: 'roo-brick-image-view',
36873 initEvents: function()
36875 if(this.title.length || this.html.length){
36876 this.el.on('mouseenter' ,this.enter, this);
36877 this.el.on('mouseleave', this.leave, this);
36880 Roo.EventManager.onWindowResize(this.resize, this);
36882 if(this.bgimage.length){
36883 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36884 this.imageEl.on('load', this.onImageLoad, this);
36891 onImageLoad : function()
36896 resize : function()
36898 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36900 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36902 if(this.bgimage.length){
36903 var image = this.el.select('.roo-brick-image-view', true).first();
36905 image.setWidth(paragraph.getWidth());
36908 image.setHeight(paragraph.getWidth());
36911 this.el.setHeight(image.getHeight());
36912 paragraph.setHeight(image.getHeight());
36918 enter: function(e, el)
36920 e.preventDefault();
36922 if(this.bgimage.length){
36923 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36924 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36928 leave: function(e, el)
36930 e.preventDefault();
36932 if(this.bgimage.length){
36933 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36934 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36949 * @class Roo.bootstrap.NumberField
36950 * @extends Roo.bootstrap.Input
36951 * Bootstrap NumberField class
36957 * Create a new NumberField
36958 * @param {Object} config The config object
36961 Roo.bootstrap.NumberField = function(config){
36962 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36965 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36968 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36970 allowDecimals : true,
36972 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36974 decimalSeparator : ".",
36976 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36978 decimalPrecision : 2,
36980 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36982 allowNegative : true,
36985 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36989 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36991 minValue : Number.NEGATIVE_INFINITY,
36993 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36995 maxValue : Number.MAX_VALUE,
36997 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36999 minText : "The minimum value for this field is {0}",
37001 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
37003 maxText : "The maximum value for this field is {0}",
37005 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
37006 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
37008 nanText : "{0} is not a valid number",
37010 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
37012 thousandsDelimiter : false,
37014 * @cfg {String} valueAlign alignment of value
37016 valueAlign : "left",
37018 getAutoCreate : function()
37020 var hiddenInput = {
37024 cls: 'hidden-number-input'
37028 hiddenInput.name = this.name;
37033 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37035 this.name = hiddenInput.name;
37037 if(cfg.cn.length > 0) {
37038 cfg.cn.push(hiddenInput);
37045 initEvents : function()
37047 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37049 var allowed = "0123456789";
37051 if(this.allowDecimals){
37052 allowed += this.decimalSeparator;
37055 if(this.allowNegative){
37059 if(this.thousandsDelimiter) {
37063 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37065 var keyPress = function(e){
37067 var k = e.getKey();
37069 var c = e.getCharCode();
37072 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37073 allowed.indexOf(String.fromCharCode(c)) === -1
37079 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37083 if(allowed.indexOf(String.fromCharCode(c)) === -1){
37088 this.el.on("keypress", keyPress, this);
37091 validateValue : function(value)
37094 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37098 var num = this.parseValue(value);
37101 this.markInvalid(String.format(this.nanText, value));
37105 if(num < this.minValue){
37106 this.markInvalid(String.format(this.minText, this.minValue));
37110 if(num > this.maxValue){
37111 this.markInvalid(String.format(this.maxText, this.maxValue));
37118 getValue : function()
37120 var v = this.hiddenEl().getValue();
37122 return this.fixPrecision(this.parseValue(v));
37125 parseValue : function(value)
37127 if(this.thousandsDelimiter) {
37129 r = new RegExp(",", "g");
37130 value = value.replace(r, "");
37133 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37134 return isNaN(value) ? '' : value;
37137 fixPrecision : function(value)
37139 if(this.thousandsDelimiter) {
37141 r = new RegExp(",", "g");
37142 value = value.replace(r, "");
37145 var nan = isNaN(value);
37147 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37148 return nan ? '' : value;
37150 return parseFloat(value).toFixed(this.decimalPrecision);
37153 setValue : function(v)
37155 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37161 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37163 this.inputEl().dom.value = (v == '') ? '' :
37164 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37166 if(!this.allowZero && v === '0') {
37167 this.hiddenEl().dom.value = '';
37168 this.inputEl().dom.value = '';
37175 decimalPrecisionFcn : function(v)
37177 return Math.floor(v);
37180 beforeBlur : function()
37182 var v = this.parseValue(this.getRawValue());
37184 if(v || v === 0 || v === ''){
37189 hiddenEl : function()
37191 return this.el.select('input.hidden-number-input',true).first();
37203 * @class Roo.bootstrap.DocumentSlider
37204 * @extends Roo.bootstrap.Component
37205 * Bootstrap DocumentSlider class
37208 * Create a new DocumentViewer
37209 * @param {Object} config The config object
37212 Roo.bootstrap.DocumentSlider = function(config){
37213 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37220 * Fire after initEvent
37221 * @param {Roo.bootstrap.DocumentSlider} this
37226 * Fire after update
37227 * @param {Roo.bootstrap.DocumentSlider} this
37233 * @param {Roo.bootstrap.DocumentSlider} this
37239 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
37245 getAutoCreate : function()
37249 cls : 'roo-document-slider',
37253 cls : 'roo-document-slider-header',
37257 cls : 'roo-document-slider-header-title'
37263 cls : 'roo-document-slider-body',
37267 cls : 'roo-document-slider-prev',
37271 cls : 'fa fa-chevron-left'
37277 cls : 'roo-document-slider-thumb',
37281 cls : 'roo-document-slider-image'
37287 cls : 'roo-document-slider-next',
37291 cls : 'fa fa-chevron-right'
37303 initEvents : function()
37305 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37306 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37308 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37309 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37311 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37312 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37314 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37315 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37317 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37318 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37320 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37321 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37323 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37324 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37326 this.thumbEl.on('click', this.onClick, this);
37328 this.prevIndicator.on('click', this.prev, this);
37330 this.nextIndicator.on('click', this.next, this);
37334 initial : function()
37336 if(this.files.length){
37337 this.indicator = 1;
37341 this.fireEvent('initial', this);
37344 update : function()
37346 this.imageEl.attr('src', this.files[this.indicator - 1]);
37348 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37350 this.prevIndicator.show();
37352 if(this.indicator == 1){
37353 this.prevIndicator.hide();
37356 this.nextIndicator.show();
37358 if(this.indicator == this.files.length){
37359 this.nextIndicator.hide();
37362 this.thumbEl.scrollTo('top');
37364 this.fireEvent('update', this);
37367 onClick : function(e)
37369 e.preventDefault();
37371 this.fireEvent('click', this);
37376 e.preventDefault();
37378 this.indicator = Math.max(1, this.indicator - 1);
37385 e.preventDefault();
37387 this.indicator = Math.min(this.files.length, this.indicator + 1);
37401 * @class Roo.bootstrap.RadioSet
37402 * @extends Roo.bootstrap.Input
37403 * Bootstrap RadioSet class
37404 * @cfg {String} indicatorpos (left|right) default left
37405 * @cfg {Boolean} inline (true|false) inline the element (default true)
37406 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37408 * Create a new RadioSet
37409 * @param {Object} config The config object
37412 Roo.bootstrap.RadioSet = function(config){
37414 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37418 Roo.bootstrap.RadioSet.register(this);
37423 * Fires when the element is checked or unchecked.
37424 * @param {Roo.bootstrap.RadioSet} this This radio
37425 * @param {Roo.bootstrap.Radio} item The checked item
37430 * Fires when the element is click.
37431 * @param {Roo.bootstrap.RadioSet} this This radio set
37432 * @param {Roo.bootstrap.Radio} item The checked item
37433 * @param {Roo.EventObject} e The event object
37440 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37448 indicatorpos : 'left',
37450 getAutoCreate : function()
37454 cls : 'roo-radio-set-label',
37458 html : this.fieldLabel
37462 if (Roo.bootstrap.version == 3) {
37465 if(this.indicatorpos == 'left'){
37468 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37469 tooltip : 'This field is required'
37474 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37475 tooltip : 'This field is required'
37481 cls : 'roo-radio-set-items'
37484 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37486 if (align === 'left' && this.fieldLabel.length) {
37489 cls : "roo-radio-set-right",
37495 if(this.labelWidth > 12){
37496 label.style = "width: " + this.labelWidth + 'px';
37499 if(this.labelWidth < 13 && this.labelmd == 0){
37500 this.labelmd = this.labelWidth;
37503 if(this.labellg > 0){
37504 label.cls += ' col-lg-' + this.labellg;
37505 items.cls += ' col-lg-' + (12 - this.labellg);
37508 if(this.labelmd > 0){
37509 label.cls += ' col-md-' + this.labelmd;
37510 items.cls += ' col-md-' + (12 - this.labelmd);
37513 if(this.labelsm > 0){
37514 label.cls += ' col-sm-' + this.labelsm;
37515 items.cls += ' col-sm-' + (12 - this.labelsm);
37518 if(this.labelxs > 0){
37519 label.cls += ' col-xs-' + this.labelxs;
37520 items.cls += ' col-xs-' + (12 - this.labelxs);
37526 cls : 'roo-radio-set',
37530 cls : 'roo-radio-set-input',
37533 value : this.value ? this.value : ''
37540 if(this.weight.length){
37541 cfg.cls += ' roo-radio-' + this.weight;
37545 cfg.cls += ' roo-radio-set-inline';
37549 ['xs','sm','md','lg'].map(function(size){
37550 if (settings[size]) {
37551 cfg.cls += ' col-' + size + '-' + settings[size];
37559 initEvents : function()
37561 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37562 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37564 if(!this.fieldLabel.length){
37565 this.labelEl.hide();
37568 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37569 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37571 this.indicator = this.indicatorEl();
37573 if(this.indicator){
37574 this.indicator.addClass('invisible');
37577 this.originalValue = this.getValue();
37581 inputEl: function ()
37583 return this.el.select('.roo-radio-set-input', true).first();
37586 getChildContainer : function()
37588 return this.itemsEl;
37591 register : function(item)
37593 this.radioes.push(item);
37597 validate : function()
37599 if(this.getVisibilityEl().hasClass('hidden')){
37605 Roo.each(this.radioes, function(i){
37614 if(this.allowBlank) {
37618 if(this.disabled || valid){
37623 this.markInvalid();
37628 markValid : function()
37630 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37631 this.indicatorEl().removeClass('visible');
37632 this.indicatorEl().addClass('invisible');
37636 if (Roo.bootstrap.version == 3) {
37637 this.el.removeClass([this.invalidClass, this.validClass]);
37638 this.el.addClass(this.validClass);
37640 this.el.removeClass(['is-invalid','is-valid']);
37641 this.el.addClass(['is-valid']);
37643 this.fireEvent('valid', this);
37646 markInvalid : function(msg)
37648 if(this.allowBlank || this.disabled){
37652 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37653 this.indicatorEl().removeClass('invisible');
37654 this.indicatorEl().addClass('visible');
37656 if (Roo.bootstrap.version == 3) {
37657 this.el.removeClass([this.invalidClass, this.validClass]);
37658 this.el.addClass(this.invalidClass);
37660 this.el.removeClass(['is-invalid','is-valid']);
37661 this.el.addClass(['is-invalid']);
37664 this.fireEvent('invalid', this, msg);
37668 setValue : function(v, suppressEvent)
37670 if(this.value === v){
37677 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37680 Roo.each(this.radioes, function(i){
37682 i.el.removeClass('checked');
37685 Roo.each(this.radioes, function(i){
37687 if(i.value === v || i.value.toString() === v.toString()){
37689 i.el.addClass('checked');
37691 if(suppressEvent !== true){
37692 this.fireEvent('check', this, i);
37703 clearInvalid : function(){
37705 if(!this.el || this.preventMark){
37709 this.el.removeClass([this.invalidClass]);
37711 this.fireEvent('valid', this);
37716 Roo.apply(Roo.bootstrap.RadioSet, {
37720 register : function(set)
37722 this.groups[set.name] = set;
37725 get: function(name)
37727 if (typeof(this.groups[name]) == 'undefined') {
37731 return this.groups[name] ;
37737 * Ext JS Library 1.1.1
37738 * Copyright(c) 2006-2007, Ext JS, LLC.
37740 * Originally Released Under LGPL - original licence link has changed is not relivant.
37743 * <script type="text/javascript">
37748 * @class Roo.bootstrap.SplitBar
37749 * @extends Roo.util.Observable
37750 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37754 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37755 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37756 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37757 split.minSize = 100;
37758 split.maxSize = 600;
37759 split.animate = true;
37760 split.on('moved', splitterMoved);
37763 * Create a new SplitBar
37764 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37765 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37766 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37767 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37768 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37769 position of the SplitBar).
37771 Roo.bootstrap.SplitBar = function(cfg){
37776 // dragElement : elm
37777 // resizingElement: el,
37779 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37780 // placement : Roo.bootstrap.SplitBar.LEFT ,
37781 // existingProxy ???
37784 this.el = Roo.get(cfg.dragElement, true);
37785 this.el.dom.unselectable = "on";
37787 this.resizingEl = Roo.get(cfg.resizingElement, true);
37791 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37792 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37795 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37798 * The minimum size of the resizing element. (Defaults to 0)
37804 * The maximum size of the resizing element. (Defaults to 2000)
37807 this.maxSize = 2000;
37810 * Whether to animate the transition to the new size
37813 this.animate = false;
37816 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37819 this.useShim = false;
37824 if(!cfg.existingProxy){
37826 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37828 this.proxy = Roo.get(cfg.existingProxy).dom;
37831 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37834 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37837 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37840 this.dragSpecs = {};
37843 * @private The adapter to use to positon and resize elements
37845 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37846 this.adapter.init(this);
37848 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37850 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37851 this.el.addClass("roo-splitbar-h");
37854 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37855 this.el.addClass("roo-splitbar-v");
37861 * Fires when the splitter is moved (alias for {@link #event-moved})
37862 * @param {Roo.bootstrap.SplitBar} this
37863 * @param {Number} newSize the new width or height
37868 * Fires when the splitter is moved
37869 * @param {Roo.bootstrap.SplitBar} this
37870 * @param {Number} newSize the new width or height
37874 * @event beforeresize
37875 * Fires before the splitter is dragged
37876 * @param {Roo.bootstrap.SplitBar} this
37878 "beforeresize" : true,
37880 "beforeapply" : true
37883 Roo.util.Observable.call(this);
37886 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37887 onStartProxyDrag : function(x, y){
37888 this.fireEvent("beforeresize", this);
37890 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37892 o.enableDisplayMode("block");
37893 // all splitbars share the same overlay
37894 Roo.bootstrap.SplitBar.prototype.overlay = o;
37896 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37897 this.overlay.show();
37898 Roo.get(this.proxy).setDisplayed("block");
37899 var size = this.adapter.getElementSize(this);
37900 this.activeMinSize = this.getMinimumSize();;
37901 this.activeMaxSize = this.getMaximumSize();;
37902 var c1 = size - this.activeMinSize;
37903 var c2 = Math.max(this.activeMaxSize - size, 0);
37904 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37905 this.dd.resetConstraints();
37906 this.dd.setXConstraint(
37907 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37908 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37910 this.dd.setYConstraint(0, 0);
37912 this.dd.resetConstraints();
37913 this.dd.setXConstraint(0, 0);
37914 this.dd.setYConstraint(
37915 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37916 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37919 this.dragSpecs.startSize = size;
37920 this.dragSpecs.startPoint = [x, y];
37921 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37925 * @private Called after the drag operation by the DDProxy
37927 onEndProxyDrag : function(e){
37928 Roo.get(this.proxy).setDisplayed(false);
37929 var endPoint = Roo.lib.Event.getXY(e);
37931 this.overlay.hide();
37934 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37935 newSize = this.dragSpecs.startSize +
37936 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37937 endPoint[0] - this.dragSpecs.startPoint[0] :
37938 this.dragSpecs.startPoint[0] - endPoint[0]
37941 newSize = this.dragSpecs.startSize +
37942 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37943 endPoint[1] - this.dragSpecs.startPoint[1] :
37944 this.dragSpecs.startPoint[1] - endPoint[1]
37947 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37948 if(newSize != this.dragSpecs.startSize){
37949 if(this.fireEvent('beforeapply', this, newSize) !== false){
37950 this.adapter.setElementSize(this, newSize);
37951 this.fireEvent("moved", this, newSize);
37952 this.fireEvent("resize", this, newSize);
37958 * Get the adapter this SplitBar uses
37959 * @return The adapter object
37961 getAdapter : function(){
37962 return this.adapter;
37966 * Set the adapter this SplitBar uses
37967 * @param {Object} adapter A SplitBar adapter object
37969 setAdapter : function(adapter){
37970 this.adapter = adapter;
37971 this.adapter.init(this);
37975 * Gets the minimum size for the resizing element
37976 * @return {Number} The minimum size
37978 getMinimumSize : function(){
37979 return this.minSize;
37983 * Sets the minimum size for the resizing element
37984 * @param {Number} minSize The minimum size
37986 setMinimumSize : function(minSize){
37987 this.minSize = minSize;
37991 * Gets the maximum size for the resizing element
37992 * @return {Number} The maximum size
37994 getMaximumSize : function(){
37995 return this.maxSize;
37999 * Sets the maximum size for the resizing element
38000 * @param {Number} maxSize The maximum size
38002 setMaximumSize : function(maxSize){
38003 this.maxSize = maxSize;
38007 * Sets the initialize size for the resizing element
38008 * @param {Number} size The initial size
38010 setCurrentSize : function(size){
38011 var oldAnimate = this.animate;
38012 this.animate = false;
38013 this.adapter.setElementSize(this, size);
38014 this.animate = oldAnimate;
38018 * Destroy this splitbar.
38019 * @param {Boolean} removeEl True to remove the element
38021 destroy : function(removeEl){
38023 this.shim.remove();
38026 this.proxy.parentNode.removeChild(this.proxy);
38034 * @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.
38036 Roo.bootstrap.SplitBar.createProxy = function(dir){
38037 var proxy = new Roo.Element(document.createElement("div"));
38038 proxy.unselectable();
38039 var cls = 'roo-splitbar-proxy';
38040 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38041 document.body.appendChild(proxy.dom);
38046 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38047 * Default Adapter. It assumes the splitter and resizing element are not positioned
38048 * elements and only gets/sets the width of the element. Generally used for table based layouts.
38050 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38053 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38054 // do nothing for now
38055 init : function(s){
38059 * Called before drag operations to get the current size of the resizing element.
38060 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38062 getElementSize : function(s){
38063 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38064 return s.resizingEl.getWidth();
38066 return s.resizingEl.getHeight();
38071 * Called after drag operations to set the size of the resizing element.
38072 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38073 * @param {Number} newSize The new size to set
38074 * @param {Function} onComplete A function to be invoked when resizing is complete
38076 setElementSize : function(s, newSize, onComplete){
38077 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38079 s.resizingEl.setWidth(newSize);
38081 onComplete(s, newSize);
38084 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38089 s.resizingEl.setHeight(newSize);
38091 onComplete(s, newSize);
38094 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38101 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38102 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38103 * Adapter that moves the splitter element to align with the resized sizing element.
38104 * Used with an absolute positioned SplitBar.
38105 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38106 * document.body, make sure you assign an id to the body element.
38108 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38109 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38110 this.container = Roo.get(container);
38113 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38114 init : function(s){
38115 this.basic.init(s);
38118 getElementSize : function(s){
38119 return this.basic.getElementSize(s);
38122 setElementSize : function(s, newSize, onComplete){
38123 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38126 moveSplitter : function(s){
38127 var yes = Roo.bootstrap.SplitBar;
38128 switch(s.placement){
38130 s.el.setX(s.resizingEl.getRight());
38133 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38136 s.el.setY(s.resizingEl.getBottom());
38139 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38146 * Orientation constant - Create a vertical SplitBar
38150 Roo.bootstrap.SplitBar.VERTICAL = 1;
38153 * Orientation constant - Create a horizontal SplitBar
38157 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38160 * Placement constant - The resizing element is to the left of the splitter element
38164 Roo.bootstrap.SplitBar.LEFT = 1;
38167 * Placement constant - The resizing element is to the right of the splitter element
38171 Roo.bootstrap.SplitBar.RIGHT = 2;
38174 * Placement constant - The resizing element is positioned above the splitter element
38178 Roo.bootstrap.SplitBar.TOP = 3;
38181 * Placement constant - The resizing element is positioned under splitter element
38185 Roo.bootstrap.SplitBar.BOTTOM = 4;
38186 Roo.namespace("Roo.bootstrap.layout");/*
38188 * Ext JS Library 1.1.1
38189 * Copyright(c) 2006-2007, Ext JS, LLC.
38191 * Originally Released Under LGPL - original licence link has changed is not relivant.
38194 * <script type="text/javascript">
38198 * @class Roo.bootstrap.layout.Manager
38199 * @extends Roo.bootstrap.Component
38200 * Base class for layout managers.
38202 Roo.bootstrap.layout.Manager = function(config)
38204 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38210 /** false to disable window resize monitoring @type Boolean */
38211 this.monitorWindowResize = true;
38216 * Fires when a layout is performed.
38217 * @param {Roo.LayoutManager} this
38221 * @event regionresized
38222 * Fires when the user resizes a region.
38223 * @param {Roo.LayoutRegion} region The resized region
38224 * @param {Number} newSize The new size (width for east/west, height for north/south)
38226 "regionresized" : true,
38228 * @event regioncollapsed
38229 * Fires when a region is collapsed.
38230 * @param {Roo.LayoutRegion} region The collapsed region
38232 "regioncollapsed" : true,
38234 * @event regionexpanded
38235 * Fires when a region is expanded.
38236 * @param {Roo.LayoutRegion} region The expanded region
38238 "regionexpanded" : true
38240 this.updating = false;
38243 this.el = Roo.get(config.el);
38249 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38254 monitorWindowResize : true,
38260 onRender : function(ct, position)
38263 this.el = Roo.get(ct);
38266 //this.fireEvent('render',this);
38270 initEvents: function()
38274 // ie scrollbar fix
38275 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38276 document.body.scroll = "no";
38277 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38278 this.el.position('relative');
38280 this.id = this.el.id;
38281 this.el.addClass("roo-layout-container");
38282 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38283 if(this.el.dom != document.body ) {
38284 this.el.on('resize', this.layout,this);
38285 this.el.on('show', this.layout,this);
38291 * Returns true if this layout is currently being updated
38292 * @return {Boolean}
38294 isUpdating : function(){
38295 return this.updating;
38299 * Suspend the LayoutManager from doing auto-layouts while
38300 * making multiple add or remove calls
38302 beginUpdate : function(){
38303 this.updating = true;
38307 * Restore auto-layouts and optionally disable the manager from performing a layout
38308 * @param {Boolean} noLayout true to disable a layout update
38310 endUpdate : function(noLayout){
38311 this.updating = false;
38317 layout: function(){
38321 onRegionResized : function(region, newSize){
38322 this.fireEvent("regionresized", region, newSize);
38326 onRegionCollapsed : function(region){
38327 this.fireEvent("regioncollapsed", region);
38330 onRegionExpanded : function(region){
38331 this.fireEvent("regionexpanded", region);
38335 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38336 * performs box-model adjustments.
38337 * @return {Object} The size as an object {width: (the width), height: (the height)}
38339 getViewSize : function()
38342 if(this.el.dom != document.body){
38343 size = this.el.getSize();
38345 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38347 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38348 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38353 * Returns the Element this layout is bound to.
38354 * @return {Roo.Element}
38356 getEl : function(){
38361 * Returns the specified region.
38362 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38363 * @return {Roo.LayoutRegion}
38365 getRegion : function(target){
38366 return this.regions[target.toLowerCase()];
38369 onWindowResize : function(){
38370 if(this.monitorWindowResize){
38377 * Ext JS Library 1.1.1
38378 * Copyright(c) 2006-2007, Ext JS, LLC.
38380 * Originally Released Under LGPL - original licence link has changed is not relivant.
38383 * <script type="text/javascript">
38386 * @class Roo.bootstrap.layout.Border
38387 * @extends Roo.bootstrap.layout.Manager
38389 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38390 * please see: examples/bootstrap/nested.html<br><br>
38392 <b>The container the layout is rendered into can be either the body element or any other element.
38393 If it is not the body element, the container needs to either be an absolute positioned element,
38394 or you will need to add "position:relative" to the css of the container. You will also need to specify
38395 the container size if it is not the body element.</b>
38398 * Create a new Border
38399 * @param {Object} config Configuration options
38401 Roo.bootstrap.layout.Border = function(config){
38402 config = config || {};
38403 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38407 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38408 if(config[region]){
38409 config[region].region = region;
38410 this.addRegion(config[region]);
38416 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38418 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38420 parent : false, // this might point to a 'nest' or a ???
38423 * Creates and adds a new region if it doesn't already exist.
38424 * @param {String} target The target region key (north, south, east, west or center).
38425 * @param {Object} config The regions config object
38426 * @return {BorderLayoutRegion} The new region
38428 addRegion : function(config)
38430 if(!this.regions[config.region]){
38431 var r = this.factory(config);
38432 this.bindRegion(r);
38434 return this.regions[config.region];
38438 bindRegion : function(r){
38439 this.regions[r.config.region] = r;
38441 r.on("visibilitychange", this.layout, this);
38442 r.on("paneladded", this.layout, this);
38443 r.on("panelremoved", this.layout, this);
38444 r.on("invalidated", this.layout, this);
38445 r.on("resized", this.onRegionResized, this);
38446 r.on("collapsed", this.onRegionCollapsed, this);
38447 r.on("expanded", this.onRegionExpanded, this);
38451 * Performs a layout update.
38453 layout : function()
38455 if(this.updating) {
38459 // render all the rebions if they have not been done alreayd?
38460 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38461 if(this.regions[region] && !this.regions[region].bodyEl){
38462 this.regions[region].onRender(this.el)
38466 var size = this.getViewSize();
38467 var w = size.width;
38468 var h = size.height;
38473 //var x = 0, y = 0;
38475 var rs = this.regions;
38476 var north = rs["north"];
38477 var south = rs["south"];
38478 var west = rs["west"];
38479 var east = rs["east"];
38480 var center = rs["center"];
38481 //if(this.hideOnLayout){ // not supported anymore
38482 //c.el.setStyle("display", "none");
38484 if(north && north.isVisible()){
38485 var b = north.getBox();
38486 var m = north.getMargins();
38487 b.width = w - (m.left+m.right);
38490 centerY = b.height + b.y + m.bottom;
38491 centerH -= centerY;
38492 north.updateBox(this.safeBox(b));
38494 if(south && south.isVisible()){
38495 var b = south.getBox();
38496 var m = south.getMargins();
38497 b.width = w - (m.left+m.right);
38499 var totalHeight = (b.height + m.top + m.bottom);
38500 b.y = h - totalHeight + m.top;
38501 centerH -= totalHeight;
38502 south.updateBox(this.safeBox(b));
38504 if(west && west.isVisible()){
38505 var b = west.getBox();
38506 var m = west.getMargins();
38507 b.height = centerH - (m.top+m.bottom);
38509 b.y = centerY + m.top;
38510 var totalWidth = (b.width + m.left + m.right);
38511 centerX += totalWidth;
38512 centerW -= totalWidth;
38513 west.updateBox(this.safeBox(b));
38515 if(east && east.isVisible()){
38516 var b = east.getBox();
38517 var m = east.getMargins();
38518 b.height = centerH - (m.top+m.bottom);
38519 var totalWidth = (b.width + m.left + m.right);
38520 b.x = w - totalWidth + m.left;
38521 b.y = centerY + m.top;
38522 centerW -= totalWidth;
38523 east.updateBox(this.safeBox(b));
38526 var m = center.getMargins();
38528 x: centerX + m.left,
38529 y: centerY + m.top,
38530 width: centerW - (m.left+m.right),
38531 height: centerH - (m.top+m.bottom)
38533 //if(this.hideOnLayout){
38534 //center.el.setStyle("display", "block");
38536 center.updateBox(this.safeBox(centerBox));
38539 this.fireEvent("layout", this);
38543 safeBox : function(box){
38544 box.width = Math.max(0, box.width);
38545 box.height = Math.max(0, box.height);
38550 * Adds a ContentPanel (or subclass) to this layout.
38551 * @param {String} target The target region key (north, south, east, west or center).
38552 * @param {Roo.ContentPanel} panel The panel to add
38553 * @return {Roo.ContentPanel} The added panel
38555 add : function(target, panel){
38557 target = target.toLowerCase();
38558 return this.regions[target].add(panel);
38562 * Remove a ContentPanel (or subclass) to this layout.
38563 * @param {String} target The target region key (north, south, east, west or center).
38564 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38565 * @return {Roo.ContentPanel} The removed panel
38567 remove : function(target, panel){
38568 target = target.toLowerCase();
38569 return this.regions[target].remove(panel);
38573 * Searches all regions for a panel with the specified id
38574 * @param {String} panelId
38575 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38577 findPanel : function(panelId){
38578 var rs = this.regions;
38579 for(var target in rs){
38580 if(typeof rs[target] != "function"){
38581 var p = rs[target].getPanel(panelId);
38591 * Searches all regions for a panel with the specified id and activates (shows) it.
38592 * @param {String/ContentPanel} panelId The panels id or the panel itself
38593 * @return {Roo.ContentPanel} The shown panel or null
38595 showPanel : function(panelId) {
38596 var rs = this.regions;
38597 for(var target in rs){
38598 var r = rs[target];
38599 if(typeof r != "function"){
38600 if(r.hasPanel(panelId)){
38601 return r.showPanel(panelId);
38609 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38610 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38613 restoreState : function(provider){
38615 provider = Roo.state.Manager;
38617 var sm = new Roo.LayoutStateManager();
38618 sm.init(this, provider);
38624 * Adds a xtype elements to the layout.
38628 xtype : 'ContentPanel',
38635 xtype : 'NestedLayoutPanel',
38641 items : [ ... list of content panels or nested layout panels.. ]
38645 * @param {Object} cfg Xtype definition of item to add.
38647 addxtype : function(cfg)
38649 // basically accepts a pannel...
38650 // can accept a layout region..!?!?
38651 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38654 // theory? children can only be panels??
38656 //if (!cfg.xtype.match(/Panel$/)) {
38661 if (typeof(cfg.region) == 'undefined') {
38662 Roo.log("Failed to add Panel, region was not set");
38666 var region = cfg.region;
38672 xitems = cfg.items;
38677 if ( region == 'center') {
38678 Roo.log("Center: " + cfg.title);
38684 case 'Content': // ContentPanel (el, cfg)
38685 case 'Scroll': // ContentPanel (el, cfg)
38687 cfg.autoCreate = cfg.autoCreate || true;
38688 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38690 // var el = this.el.createChild();
38691 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38694 this.add(region, ret);
38698 case 'TreePanel': // our new panel!
38699 cfg.el = this.el.createChild();
38700 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38701 this.add(region, ret);
38706 // create a new Layout (which is a Border Layout...
38708 var clayout = cfg.layout;
38709 clayout.el = this.el.createChild();
38710 clayout.items = clayout.items || [];
38714 // replace this exitems with the clayout ones..
38715 xitems = clayout.items;
38717 // force background off if it's in center...
38718 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38719 cfg.background = false;
38721 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38724 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38725 //console.log('adding nested layout panel ' + cfg.toSource());
38726 this.add(region, ret);
38727 nb = {}; /// find first...
38732 // needs grid and region
38734 //var el = this.getRegion(region).el.createChild();
38736 *var el = this.el.createChild();
38737 // create the grid first...
38738 cfg.grid.container = el;
38739 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38742 if (region == 'center' && this.active ) {
38743 cfg.background = false;
38746 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38748 this.add(region, ret);
38750 if (cfg.background) {
38751 // render grid on panel activation (if panel background)
38752 ret.on('activate', function(gp) {
38753 if (!gp.grid.rendered) {
38754 // gp.grid.render(el);
38758 // cfg.grid.render(el);
38764 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38765 // it was the old xcomponent building that caused this before.
38766 // espeically if border is the top element in the tree.
38776 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38778 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38779 this.add(region, ret);
38783 throw "Can not add '" + cfg.xtype + "' to Border";
38789 this.beginUpdate();
38793 Roo.each(xitems, function(i) {
38794 region = nb && i.region ? i.region : false;
38796 var add = ret.addxtype(i);
38799 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38800 if (!i.background) {
38801 abn[region] = nb[region] ;
38808 // make the last non-background panel active..
38809 //if (nb) { Roo.log(abn); }
38812 for(var r in abn) {
38813 region = this.getRegion(r);
38815 // tried using nb[r], but it does not work..
38817 region.showPanel(abn[r]);
38828 factory : function(cfg)
38831 var validRegions = Roo.bootstrap.layout.Border.regions;
38833 var target = cfg.region;
38836 var r = Roo.bootstrap.layout;
38840 return new r.North(cfg);
38842 return new r.South(cfg);
38844 return new r.East(cfg);
38846 return new r.West(cfg);
38848 return new r.Center(cfg);
38850 throw 'Layout region "'+target+'" not supported.';
38857 * Ext JS Library 1.1.1
38858 * Copyright(c) 2006-2007, Ext JS, LLC.
38860 * Originally Released Under LGPL - original licence link has changed is not relivant.
38863 * <script type="text/javascript">
38867 * @class Roo.bootstrap.layout.Basic
38868 * @extends Roo.util.Observable
38869 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38870 * and does not have a titlebar, tabs or any other features. All it does is size and position
38871 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38872 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38873 * @cfg {string} region the region that it inhabits..
38874 * @cfg {bool} skipConfig skip config?
38878 Roo.bootstrap.layout.Basic = function(config){
38880 this.mgr = config.mgr;
38882 this.position = config.region;
38884 var skipConfig = config.skipConfig;
38888 * @scope Roo.BasicLayoutRegion
38892 * @event beforeremove
38893 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38894 * @param {Roo.LayoutRegion} this
38895 * @param {Roo.ContentPanel} panel The panel
38896 * @param {Object} e The cancel event object
38898 "beforeremove" : true,
38900 * @event invalidated
38901 * Fires when the layout for this region is changed.
38902 * @param {Roo.LayoutRegion} this
38904 "invalidated" : true,
38906 * @event visibilitychange
38907 * Fires when this region is shown or hidden
38908 * @param {Roo.LayoutRegion} this
38909 * @param {Boolean} visibility true or false
38911 "visibilitychange" : true,
38913 * @event paneladded
38914 * Fires when a panel is added.
38915 * @param {Roo.LayoutRegion} this
38916 * @param {Roo.ContentPanel} panel The panel
38918 "paneladded" : true,
38920 * @event panelremoved
38921 * Fires when a panel is removed.
38922 * @param {Roo.LayoutRegion} this
38923 * @param {Roo.ContentPanel} panel The panel
38925 "panelremoved" : true,
38927 * @event beforecollapse
38928 * Fires when this region before collapse.
38929 * @param {Roo.LayoutRegion} this
38931 "beforecollapse" : true,
38934 * Fires when this region is collapsed.
38935 * @param {Roo.LayoutRegion} this
38937 "collapsed" : true,
38940 * Fires when this region is expanded.
38941 * @param {Roo.LayoutRegion} this
38946 * Fires when this region is slid into view.
38947 * @param {Roo.LayoutRegion} this
38949 "slideshow" : true,
38952 * Fires when this region slides out of view.
38953 * @param {Roo.LayoutRegion} this
38955 "slidehide" : true,
38957 * @event panelactivated
38958 * Fires when a panel is activated.
38959 * @param {Roo.LayoutRegion} this
38960 * @param {Roo.ContentPanel} panel The activated panel
38962 "panelactivated" : true,
38965 * Fires when the user resizes this region.
38966 * @param {Roo.LayoutRegion} this
38967 * @param {Number} newSize The new size (width for east/west, height for north/south)
38971 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38972 this.panels = new Roo.util.MixedCollection();
38973 this.panels.getKey = this.getPanelId.createDelegate(this);
38975 this.activePanel = null;
38976 // ensure listeners are added...
38978 if (config.listeners || config.events) {
38979 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38980 listeners : config.listeners || {},
38981 events : config.events || {}
38985 if(skipConfig !== true){
38986 this.applyConfig(config);
38990 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38992 getPanelId : function(p){
38996 applyConfig : function(config){
38997 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38998 this.config = config;
39003 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
39004 * the width, for horizontal (north, south) the height.
39005 * @param {Number} newSize The new width or height
39007 resizeTo : function(newSize){
39008 var el = this.el ? this.el :
39009 (this.activePanel ? this.activePanel.getEl() : null);
39011 switch(this.position){
39014 el.setWidth(newSize);
39015 this.fireEvent("resized", this, newSize);
39019 el.setHeight(newSize);
39020 this.fireEvent("resized", this, newSize);
39026 getBox : function(){
39027 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39030 getMargins : function(){
39031 return this.margins;
39034 updateBox : function(box){
39036 var el = this.activePanel.getEl();
39037 el.dom.style.left = box.x + "px";
39038 el.dom.style.top = box.y + "px";
39039 this.activePanel.setSize(box.width, box.height);
39043 * Returns the container element for this region.
39044 * @return {Roo.Element}
39046 getEl : function(){
39047 return this.activePanel;
39051 * Returns true if this region is currently visible.
39052 * @return {Boolean}
39054 isVisible : function(){
39055 return this.activePanel ? true : false;
39058 setActivePanel : function(panel){
39059 panel = this.getPanel(panel);
39060 if(this.activePanel && this.activePanel != panel){
39061 this.activePanel.setActiveState(false);
39062 this.activePanel.getEl().setLeftTop(-10000,-10000);
39064 this.activePanel = panel;
39065 panel.setActiveState(true);
39067 panel.setSize(this.box.width, this.box.height);
39069 this.fireEvent("panelactivated", this, panel);
39070 this.fireEvent("invalidated");
39074 * Show the specified panel.
39075 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39076 * @return {Roo.ContentPanel} The shown panel or null
39078 showPanel : function(panel){
39079 panel = this.getPanel(panel);
39081 this.setActivePanel(panel);
39087 * Get the active panel for this region.
39088 * @return {Roo.ContentPanel} The active panel or null
39090 getActivePanel : function(){
39091 return this.activePanel;
39095 * Add the passed ContentPanel(s)
39096 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39097 * @return {Roo.ContentPanel} The panel added (if only one was added)
39099 add : function(panel){
39100 if(arguments.length > 1){
39101 for(var i = 0, len = arguments.length; i < len; i++) {
39102 this.add(arguments[i]);
39106 if(this.hasPanel(panel)){
39107 this.showPanel(panel);
39110 var el = panel.getEl();
39111 if(el.dom.parentNode != this.mgr.el.dom){
39112 this.mgr.el.dom.appendChild(el.dom);
39114 if(panel.setRegion){
39115 panel.setRegion(this);
39117 this.panels.add(panel);
39118 el.setStyle("position", "absolute");
39119 if(!panel.background){
39120 this.setActivePanel(panel);
39121 if(this.config.initialSize && this.panels.getCount()==1){
39122 this.resizeTo(this.config.initialSize);
39125 this.fireEvent("paneladded", this, panel);
39130 * Returns true if the panel is in this region.
39131 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39132 * @return {Boolean}
39134 hasPanel : function(panel){
39135 if(typeof panel == "object"){ // must be panel obj
39136 panel = panel.getId();
39138 return this.getPanel(panel) ? true : false;
39142 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39143 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39144 * @param {Boolean} preservePanel Overrides the config preservePanel option
39145 * @return {Roo.ContentPanel} The panel that was removed
39147 remove : function(panel, preservePanel){
39148 panel = this.getPanel(panel);
39153 this.fireEvent("beforeremove", this, panel, e);
39154 if(e.cancel === true){
39157 var panelId = panel.getId();
39158 this.panels.removeKey(panelId);
39163 * Returns the panel specified or null if it's not in this region.
39164 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39165 * @return {Roo.ContentPanel}
39167 getPanel : function(id){
39168 if(typeof id == "object"){ // must be panel obj
39171 return this.panels.get(id);
39175 * Returns this regions position (north/south/east/west/center).
39178 getPosition: function(){
39179 return this.position;
39183 * Ext JS Library 1.1.1
39184 * Copyright(c) 2006-2007, Ext JS, LLC.
39186 * Originally Released Under LGPL - original licence link has changed is not relivant.
39189 * <script type="text/javascript">
39193 * @class Roo.bootstrap.layout.Region
39194 * @extends Roo.bootstrap.layout.Basic
39195 * This class represents a region in a layout manager.
39197 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39198 * @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})
39199 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
39200 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
39201 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
39202 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
39203 * @cfg {String} title The title for the region (overrides panel titles)
39204 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
39205 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39206 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
39207 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39208 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
39209 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39210 * the space available, similar to FireFox 1.5 tabs (defaults to false)
39211 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
39212 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
39213 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
39215 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
39216 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
39217 * @cfg {Boolean} disableTabTips True to disable tab tooltips
39218 * @cfg {Number} width For East/West panels
39219 * @cfg {Number} height For North/South panels
39220 * @cfg {Boolean} split To show the splitter
39221 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
39223 * @cfg {string} cls Extra CSS classes to add to region
39225 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
39226 * @cfg {string} region the region that it inhabits..
39229 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
39230 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
39232 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
39233 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
39234 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
39236 Roo.bootstrap.layout.Region = function(config)
39238 this.applyConfig(config);
39240 var mgr = config.mgr;
39241 var pos = config.region;
39242 config.skipConfig = true;
39243 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39246 this.onRender(mgr.el);
39249 this.visible = true;
39250 this.collapsed = false;
39251 this.unrendered_panels = [];
39254 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39256 position: '', // set by wrapper (eg. north/south etc..)
39257 unrendered_panels : null, // unrendered panels.
39259 tabPosition : false,
39261 mgr: false, // points to 'Border'
39264 createBody : function(){
39265 /** This region's body element
39266 * @type Roo.Element */
39267 this.bodyEl = this.el.createChild({
39269 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39273 onRender: function(ctr, pos)
39275 var dh = Roo.DomHelper;
39276 /** This region's container element
39277 * @type Roo.Element */
39278 this.el = dh.append(ctr.dom, {
39280 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39282 /** This region's title element
39283 * @type Roo.Element */
39285 this.titleEl = dh.append(this.el.dom, {
39287 unselectable: "on",
39288 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39290 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
39291 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39295 this.titleEl.enableDisplayMode();
39296 /** This region's title text element
39297 * @type HTMLElement */
39298 this.titleTextEl = this.titleEl.dom.firstChild;
39299 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39301 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39302 this.closeBtn.enableDisplayMode();
39303 this.closeBtn.on("click", this.closeClicked, this);
39304 this.closeBtn.hide();
39306 this.createBody(this.config);
39307 if(this.config.hideWhenEmpty){
39309 this.on("paneladded", this.validateVisibility, this);
39310 this.on("panelremoved", this.validateVisibility, this);
39312 if(this.autoScroll){
39313 this.bodyEl.setStyle("overflow", "auto");
39315 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39317 //if(c.titlebar !== false){
39318 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39319 this.titleEl.hide();
39321 this.titleEl.show();
39322 if(this.config.title){
39323 this.titleTextEl.innerHTML = this.config.title;
39327 if(this.config.collapsed){
39328 this.collapse(true);
39330 if(this.config.hidden){
39334 if (this.unrendered_panels && this.unrendered_panels.length) {
39335 for (var i =0;i< this.unrendered_panels.length; i++) {
39336 this.add(this.unrendered_panels[i]);
39338 this.unrendered_panels = null;
39344 applyConfig : function(c)
39347 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39348 var dh = Roo.DomHelper;
39349 if(c.titlebar !== false){
39350 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39351 this.collapseBtn.on("click", this.collapse, this);
39352 this.collapseBtn.enableDisplayMode();
39354 if(c.showPin === true || this.showPin){
39355 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39356 this.stickBtn.enableDisplayMode();
39357 this.stickBtn.on("click", this.expand, this);
39358 this.stickBtn.hide();
39363 /** This region's collapsed element
39364 * @type Roo.Element */
39367 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39368 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39371 if(c.floatable !== false){
39372 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39373 this.collapsedEl.on("click", this.collapseClick, this);
39376 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39377 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39378 id: "message", unselectable: "on", style:{"float":"left"}});
39379 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39381 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39382 this.expandBtn.on("click", this.expand, this);
39386 if(this.collapseBtn){
39387 this.collapseBtn.setVisible(c.collapsible == true);
39390 this.cmargins = c.cmargins || this.cmargins ||
39391 (this.position == "west" || this.position == "east" ?
39392 {top: 0, left: 2, right:2, bottom: 0} :
39393 {top: 2, left: 0, right:0, bottom: 2});
39395 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39398 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39400 this.autoScroll = c.autoScroll || false;
39405 this.duration = c.duration || .30;
39406 this.slideDuration = c.slideDuration || .45;
39411 * Returns true if this region is currently visible.
39412 * @return {Boolean}
39414 isVisible : function(){
39415 return this.visible;
39419 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39420 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39422 //setCollapsedTitle : function(title){
39423 // title = title || " ";
39424 // if(this.collapsedTitleTextEl){
39425 // this.collapsedTitleTextEl.innerHTML = title;
39429 getBox : function(){
39431 // if(!this.collapsed){
39432 b = this.el.getBox(false, true);
39434 // b = this.collapsedEl.getBox(false, true);
39439 getMargins : function(){
39440 return this.margins;
39441 //return this.collapsed ? this.cmargins : this.margins;
39444 highlight : function(){
39445 this.el.addClass("x-layout-panel-dragover");
39448 unhighlight : function(){
39449 this.el.removeClass("x-layout-panel-dragover");
39452 updateBox : function(box)
39454 if (!this.bodyEl) {
39455 return; // not rendered yet..
39459 if(!this.collapsed){
39460 this.el.dom.style.left = box.x + "px";
39461 this.el.dom.style.top = box.y + "px";
39462 this.updateBody(box.width, box.height);
39464 this.collapsedEl.dom.style.left = box.x + "px";
39465 this.collapsedEl.dom.style.top = box.y + "px";
39466 this.collapsedEl.setSize(box.width, box.height);
39469 this.tabs.autoSizeTabs();
39473 updateBody : function(w, h)
39476 this.el.setWidth(w);
39477 w -= this.el.getBorderWidth("rl");
39478 if(this.config.adjustments){
39479 w += this.config.adjustments[0];
39482 if(h !== null && h > 0){
39483 this.el.setHeight(h);
39484 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39485 h -= this.el.getBorderWidth("tb");
39486 if(this.config.adjustments){
39487 h += this.config.adjustments[1];
39489 this.bodyEl.setHeight(h);
39491 h = this.tabs.syncHeight(h);
39494 if(this.panelSize){
39495 w = w !== null ? w : this.panelSize.width;
39496 h = h !== null ? h : this.panelSize.height;
39498 if(this.activePanel){
39499 var el = this.activePanel.getEl();
39500 w = w !== null ? w : el.getWidth();
39501 h = h !== null ? h : el.getHeight();
39502 this.panelSize = {width: w, height: h};
39503 this.activePanel.setSize(w, h);
39505 if(Roo.isIE && this.tabs){
39506 this.tabs.el.repaint();
39511 * Returns the container element for this region.
39512 * @return {Roo.Element}
39514 getEl : function(){
39519 * Hides this region.
39522 //if(!this.collapsed){
39523 this.el.dom.style.left = "-2000px";
39526 // this.collapsedEl.dom.style.left = "-2000px";
39527 // this.collapsedEl.hide();
39529 this.visible = false;
39530 this.fireEvent("visibilitychange", this, false);
39534 * Shows this region if it was previously hidden.
39537 //if(!this.collapsed){
39540 // this.collapsedEl.show();
39542 this.visible = true;
39543 this.fireEvent("visibilitychange", this, true);
39546 closeClicked : function(){
39547 if(this.activePanel){
39548 this.remove(this.activePanel);
39552 collapseClick : function(e){
39554 e.stopPropagation();
39557 e.stopPropagation();
39563 * Collapses this region.
39564 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39567 collapse : function(skipAnim, skipCheck = false){
39568 if(this.collapsed) {
39572 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39574 this.collapsed = true;
39576 this.split.el.hide();
39578 if(this.config.animate && skipAnim !== true){
39579 this.fireEvent("invalidated", this);
39580 this.animateCollapse();
39582 this.el.setLocation(-20000,-20000);
39584 this.collapsedEl.show();
39585 this.fireEvent("collapsed", this);
39586 this.fireEvent("invalidated", this);
39592 animateCollapse : function(){
39597 * Expands this region if it was previously collapsed.
39598 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39599 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39602 expand : function(e, skipAnim){
39604 e.stopPropagation();
39606 if(!this.collapsed || this.el.hasActiveFx()) {
39610 this.afterSlideIn();
39613 this.collapsed = false;
39614 if(this.config.animate && skipAnim !== true){
39615 this.animateExpand();
39619 this.split.el.show();
39621 this.collapsedEl.setLocation(-2000,-2000);
39622 this.collapsedEl.hide();
39623 this.fireEvent("invalidated", this);
39624 this.fireEvent("expanded", this);
39628 animateExpand : function(){
39632 initTabs : function()
39634 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39636 var ts = new Roo.bootstrap.panel.Tabs({
39637 el: this.bodyEl.dom,
39639 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39640 disableTooltips: this.config.disableTabTips,
39641 toolbar : this.config.toolbar
39644 if(this.config.hideTabs){
39645 ts.stripWrap.setDisplayed(false);
39648 ts.resizeTabs = this.config.resizeTabs === true;
39649 ts.minTabWidth = this.config.minTabWidth || 40;
39650 ts.maxTabWidth = this.config.maxTabWidth || 250;
39651 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39652 ts.monitorResize = false;
39653 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39654 ts.bodyEl.addClass('roo-layout-tabs-body');
39655 this.panels.each(this.initPanelAsTab, this);
39658 initPanelAsTab : function(panel){
39659 var ti = this.tabs.addTab(
39663 this.config.closeOnTab && panel.isClosable(),
39666 if(panel.tabTip !== undefined){
39667 ti.setTooltip(panel.tabTip);
39669 ti.on("activate", function(){
39670 this.setActivePanel(panel);
39673 if(this.config.closeOnTab){
39674 ti.on("beforeclose", function(t, e){
39676 this.remove(panel);
39680 panel.tabItem = ti;
39685 updatePanelTitle : function(panel, title)
39687 if(this.activePanel == panel){
39688 this.updateTitle(title);
39691 var ti = this.tabs.getTab(panel.getEl().id);
39693 if(panel.tabTip !== undefined){
39694 ti.setTooltip(panel.tabTip);
39699 updateTitle : function(title){
39700 if(this.titleTextEl && !this.config.title){
39701 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39705 setActivePanel : function(panel)
39707 panel = this.getPanel(panel);
39708 if(this.activePanel && this.activePanel != panel){
39709 if(this.activePanel.setActiveState(false) === false){
39713 this.activePanel = panel;
39714 panel.setActiveState(true);
39715 if(this.panelSize){
39716 panel.setSize(this.panelSize.width, this.panelSize.height);
39719 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39721 this.updateTitle(panel.getTitle());
39723 this.fireEvent("invalidated", this);
39725 this.fireEvent("panelactivated", this, panel);
39729 * Shows the specified panel.
39730 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39731 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39733 showPanel : function(panel)
39735 panel = this.getPanel(panel);
39738 var tab = this.tabs.getTab(panel.getEl().id);
39739 if(tab.isHidden()){
39740 this.tabs.unhideTab(tab.id);
39744 this.setActivePanel(panel);
39751 * Get the active panel for this region.
39752 * @return {Roo.ContentPanel} The active panel or null
39754 getActivePanel : function(){
39755 return this.activePanel;
39758 validateVisibility : function(){
39759 if(this.panels.getCount() < 1){
39760 this.updateTitle(" ");
39761 this.closeBtn.hide();
39764 if(!this.isVisible()){
39771 * Adds the passed ContentPanel(s) to this region.
39772 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39773 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39775 add : function(panel)
39777 if(arguments.length > 1){
39778 for(var i = 0, len = arguments.length; i < len; i++) {
39779 this.add(arguments[i]);
39784 // if we have not been rendered yet, then we can not really do much of this..
39785 if (!this.bodyEl) {
39786 this.unrendered_panels.push(panel);
39793 if(this.hasPanel(panel)){
39794 this.showPanel(panel);
39797 panel.setRegion(this);
39798 this.panels.add(panel);
39799 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39800 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39801 // and hide them... ???
39802 this.bodyEl.dom.appendChild(panel.getEl().dom);
39803 if(panel.background !== true){
39804 this.setActivePanel(panel);
39806 this.fireEvent("paneladded", this, panel);
39813 this.initPanelAsTab(panel);
39817 if(panel.background !== true){
39818 this.tabs.activate(panel.getEl().id);
39820 this.fireEvent("paneladded", this, panel);
39825 * Hides the tab for the specified panel.
39826 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39828 hidePanel : function(panel){
39829 if(this.tabs && (panel = this.getPanel(panel))){
39830 this.tabs.hideTab(panel.getEl().id);
39835 * Unhides the tab for a previously hidden panel.
39836 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39838 unhidePanel : function(panel){
39839 if(this.tabs && (panel = this.getPanel(panel))){
39840 this.tabs.unhideTab(panel.getEl().id);
39844 clearPanels : function(){
39845 while(this.panels.getCount() > 0){
39846 this.remove(this.panels.first());
39851 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39852 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39853 * @param {Boolean} preservePanel Overrides the config preservePanel option
39854 * @return {Roo.ContentPanel} The panel that was removed
39856 remove : function(panel, preservePanel)
39858 panel = this.getPanel(panel);
39863 this.fireEvent("beforeremove", this, panel, e);
39864 if(e.cancel === true){
39867 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39868 var panelId = panel.getId();
39869 this.panels.removeKey(panelId);
39871 document.body.appendChild(panel.getEl().dom);
39874 this.tabs.removeTab(panel.getEl().id);
39875 }else if (!preservePanel){
39876 this.bodyEl.dom.removeChild(panel.getEl().dom);
39878 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39879 var p = this.panels.first();
39880 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39881 tempEl.appendChild(p.getEl().dom);
39882 this.bodyEl.update("");
39883 this.bodyEl.dom.appendChild(p.getEl().dom);
39885 this.updateTitle(p.getTitle());
39887 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39888 this.setActivePanel(p);
39890 panel.setRegion(null);
39891 if(this.activePanel == panel){
39892 this.activePanel = null;
39894 if(this.config.autoDestroy !== false && preservePanel !== true){
39895 try{panel.destroy();}catch(e){}
39897 this.fireEvent("panelremoved", this, panel);
39902 * Returns the TabPanel component used by this region
39903 * @return {Roo.TabPanel}
39905 getTabs : function(){
39909 createTool : function(parentEl, className){
39910 var btn = Roo.DomHelper.append(parentEl, {
39912 cls: "x-layout-tools-button",
39915 cls: "roo-layout-tools-button-inner " + className,
39919 btn.addClassOnOver("roo-layout-tools-button-over");
39924 * Ext JS Library 1.1.1
39925 * Copyright(c) 2006-2007, Ext JS, LLC.
39927 * Originally Released Under LGPL - original licence link has changed is not relivant.
39930 * <script type="text/javascript">
39936 * @class Roo.SplitLayoutRegion
39937 * @extends Roo.LayoutRegion
39938 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39940 Roo.bootstrap.layout.Split = function(config){
39941 this.cursor = config.cursor;
39942 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39945 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39947 splitTip : "Drag to resize.",
39948 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39949 useSplitTips : false,
39951 applyConfig : function(config){
39952 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39955 onRender : function(ctr,pos) {
39957 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39958 if(!this.config.split){
39963 var splitEl = Roo.DomHelper.append(ctr.dom, {
39965 id: this.el.id + "-split",
39966 cls: "roo-layout-split roo-layout-split-"+this.position,
39969 /** The SplitBar for this region
39970 * @type Roo.SplitBar */
39971 // does not exist yet...
39972 Roo.log([this.position, this.orientation]);
39974 this.split = new Roo.bootstrap.SplitBar({
39975 dragElement : splitEl,
39976 resizingElement: this.el,
39977 orientation : this.orientation
39980 this.split.on("moved", this.onSplitMove, this);
39981 this.split.useShim = this.config.useShim === true;
39982 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39983 if(this.useSplitTips){
39984 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39986 //if(config.collapsible){
39987 // this.split.el.on("dblclick", this.collapse, this);
39990 if(typeof this.config.minSize != "undefined"){
39991 this.split.minSize = this.config.minSize;
39993 if(typeof this.config.maxSize != "undefined"){
39994 this.split.maxSize = this.config.maxSize;
39996 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39997 this.hideSplitter();
40002 getHMaxSize : function(){
40003 var cmax = this.config.maxSize || 10000;
40004 var center = this.mgr.getRegion("center");
40005 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
40008 getVMaxSize : function(){
40009 var cmax = this.config.maxSize || 10000;
40010 var center = this.mgr.getRegion("center");
40011 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
40014 onSplitMove : function(split, newSize){
40015 this.fireEvent("resized", this, newSize);
40019 * Returns the {@link Roo.SplitBar} for this region.
40020 * @return {Roo.SplitBar}
40022 getSplitBar : function(){
40027 this.hideSplitter();
40028 Roo.bootstrap.layout.Split.superclass.hide.call(this);
40031 hideSplitter : function(){
40033 this.split.el.setLocation(-2000,-2000);
40034 this.split.el.hide();
40040 this.split.el.show();
40042 Roo.bootstrap.layout.Split.superclass.show.call(this);
40045 beforeSlide: function(){
40046 if(Roo.isGecko){// firefox overflow auto bug workaround
40047 this.bodyEl.clip();
40049 this.tabs.bodyEl.clip();
40051 if(this.activePanel){
40052 this.activePanel.getEl().clip();
40054 if(this.activePanel.beforeSlide){
40055 this.activePanel.beforeSlide();
40061 afterSlide : function(){
40062 if(Roo.isGecko){// firefox overflow auto bug workaround
40063 this.bodyEl.unclip();
40065 this.tabs.bodyEl.unclip();
40067 if(this.activePanel){
40068 this.activePanel.getEl().unclip();
40069 if(this.activePanel.afterSlide){
40070 this.activePanel.afterSlide();
40076 initAutoHide : function(){
40077 if(this.autoHide !== false){
40078 if(!this.autoHideHd){
40079 var st = new Roo.util.DelayedTask(this.slideIn, this);
40080 this.autoHideHd = {
40081 "mouseout": function(e){
40082 if(!e.within(this.el, true)){
40086 "mouseover" : function(e){
40092 this.el.on(this.autoHideHd);
40096 clearAutoHide : function(){
40097 if(this.autoHide !== false){
40098 this.el.un("mouseout", this.autoHideHd.mouseout);
40099 this.el.un("mouseover", this.autoHideHd.mouseover);
40103 clearMonitor : function(){
40104 Roo.get(document).un("click", this.slideInIf, this);
40107 // these names are backwards but not changed for compat
40108 slideOut : function(){
40109 if(this.isSlid || this.el.hasActiveFx()){
40112 this.isSlid = true;
40113 if(this.collapseBtn){
40114 this.collapseBtn.hide();
40116 this.closeBtnState = this.closeBtn.getStyle('display');
40117 this.closeBtn.hide();
40119 this.stickBtn.show();
40122 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40123 this.beforeSlide();
40124 this.el.setStyle("z-index", 10001);
40125 this.el.slideIn(this.getSlideAnchor(), {
40126 callback: function(){
40128 this.initAutoHide();
40129 Roo.get(document).on("click", this.slideInIf, this);
40130 this.fireEvent("slideshow", this);
40137 afterSlideIn : function(){
40138 this.clearAutoHide();
40139 this.isSlid = false;
40140 this.clearMonitor();
40141 this.el.setStyle("z-index", "");
40142 if(this.collapseBtn){
40143 this.collapseBtn.show();
40145 this.closeBtn.setStyle('display', this.closeBtnState);
40147 this.stickBtn.hide();
40149 this.fireEvent("slidehide", this);
40152 slideIn : function(cb){
40153 if(!this.isSlid || this.el.hasActiveFx()){
40157 this.isSlid = false;
40158 this.beforeSlide();
40159 this.el.slideOut(this.getSlideAnchor(), {
40160 callback: function(){
40161 this.el.setLeftTop(-10000, -10000);
40163 this.afterSlideIn();
40171 slideInIf : function(e){
40172 if(!e.within(this.el)){
40177 animateCollapse : function(){
40178 this.beforeSlide();
40179 this.el.setStyle("z-index", 20000);
40180 var anchor = this.getSlideAnchor();
40181 this.el.slideOut(anchor, {
40182 callback : function(){
40183 this.el.setStyle("z-index", "");
40184 this.collapsedEl.slideIn(anchor, {duration:.3});
40186 this.el.setLocation(-10000,-10000);
40188 this.fireEvent("collapsed", this);
40195 animateExpand : function(){
40196 this.beforeSlide();
40197 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40198 this.el.setStyle("z-index", 20000);
40199 this.collapsedEl.hide({
40202 this.el.slideIn(this.getSlideAnchor(), {
40203 callback : function(){
40204 this.el.setStyle("z-index", "");
40207 this.split.el.show();
40209 this.fireEvent("invalidated", this);
40210 this.fireEvent("expanded", this);
40238 getAnchor : function(){
40239 return this.anchors[this.position];
40242 getCollapseAnchor : function(){
40243 return this.canchors[this.position];
40246 getSlideAnchor : function(){
40247 return this.sanchors[this.position];
40250 getAlignAdj : function(){
40251 var cm = this.cmargins;
40252 switch(this.position){
40268 getExpandAdj : function(){
40269 var c = this.collapsedEl, cm = this.cmargins;
40270 switch(this.position){
40272 return [-(cm.right+c.getWidth()+cm.left), 0];
40275 return [cm.right+c.getWidth()+cm.left, 0];
40278 return [0, -(cm.top+cm.bottom+c.getHeight())];
40281 return [0, cm.top+cm.bottom+c.getHeight()];
40287 * Ext JS Library 1.1.1
40288 * Copyright(c) 2006-2007, Ext JS, LLC.
40290 * Originally Released Under LGPL - original licence link has changed is not relivant.
40293 * <script type="text/javascript">
40296 * These classes are private internal classes
40298 Roo.bootstrap.layout.Center = function(config){
40299 config.region = "center";
40300 Roo.bootstrap.layout.Region.call(this, config);
40301 this.visible = true;
40302 this.minWidth = config.minWidth || 20;
40303 this.minHeight = config.minHeight || 20;
40306 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40308 // center panel can't be hidden
40312 // center panel can't be hidden
40315 getMinWidth: function(){
40316 return this.minWidth;
40319 getMinHeight: function(){
40320 return this.minHeight;
40334 Roo.bootstrap.layout.North = function(config)
40336 config.region = 'north';
40337 config.cursor = 'n-resize';
40339 Roo.bootstrap.layout.Split.call(this, config);
40343 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40344 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40345 this.split.el.addClass("roo-layout-split-v");
40347 //var size = config.initialSize || config.height;
40348 //if(this.el && typeof size != "undefined"){
40349 // this.el.setHeight(size);
40352 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40354 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40357 onRender : function(ctr, pos)
40359 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40360 var size = this.config.initialSize || this.config.height;
40361 if(this.el && typeof size != "undefined"){
40362 this.el.setHeight(size);
40367 getBox : function(){
40368 if(this.collapsed){
40369 return this.collapsedEl.getBox();
40371 var box = this.el.getBox();
40373 box.height += this.split.el.getHeight();
40378 updateBox : function(box){
40379 if(this.split && !this.collapsed){
40380 box.height -= this.split.el.getHeight();
40381 this.split.el.setLeft(box.x);
40382 this.split.el.setTop(box.y+box.height);
40383 this.split.el.setWidth(box.width);
40385 if(this.collapsed){
40386 this.updateBody(box.width, null);
40388 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40396 Roo.bootstrap.layout.South = function(config){
40397 config.region = 'south';
40398 config.cursor = 's-resize';
40399 Roo.bootstrap.layout.Split.call(this, config);
40401 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40402 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40403 this.split.el.addClass("roo-layout-split-v");
40408 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40409 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40411 onRender : function(ctr, pos)
40413 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40414 var size = this.config.initialSize || this.config.height;
40415 if(this.el && typeof size != "undefined"){
40416 this.el.setHeight(size);
40421 getBox : function(){
40422 if(this.collapsed){
40423 return this.collapsedEl.getBox();
40425 var box = this.el.getBox();
40427 var sh = this.split.el.getHeight();
40434 updateBox : function(box){
40435 if(this.split && !this.collapsed){
40436 var sh = this.split.el.getHeight();
40439 this.split.el.setLeft(box.x);
40440 this.split.el.setTop(box.y-sh);
40441 this.split.el.setWidth(box.width);
40443 if(this.collapsed){
40444 this.updateBody(box.width, null);
40446 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40450 Roo.bootstrap.layout.East = function(config){
40451 config.region = "east";
40452 config.cursor = "e-resize";
40453 Roo.bootstrap.layout.Split.call(this, config);
40455 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40456 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40457 this.split.el.addClass("roo-layout-split-h");
40461 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40462 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40464 onRender : function(ctr, pos)
40466 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40467 var size = this.config.initialSize || this.config.width;
40468 if(this.el && typeof size != "undefined"){
40469 this.el.setWidth(size);
40474 getBox : function(){
40475 if(this.collapsed){
40476 return this.collapsedEl.getBox();
40478 var box = this.el.getBox();
40480 var sw = this.split.el.getWidth();
40487 updateBox : function(box){
40488 if(this.split && !this.collapsed){
40489 var sw = this.split.el.getWidth();
40491 this.split.el.setLeft(box.x);
40492 this.split.el.setTop(box.y);
40493 this.split.el.setHeight(box.height);
40496 if(this.collapsed){
40497 this.updateBody(null, box.height);
40499 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40503 Roo.bootstrap.layout.West = function(config){
40504 config.region = "west";
40505 config.cursor = "w-resize";
40507 Roo.bootstrap.layout.Split.call(this, config);
40509 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40510 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40511 this.split.el.addClass("roo-layout-split-h");
40515 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40516 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40518 onRender: function(ctr, pos)
40520 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40521 var size = this.config.initialSize || this.config.width;
40522 if(typeof size != "undefined"){
40523 this.el.setWidth(size);
40527 getBox : function(){
40528 if(this.collapsed){
40529 return this.collapsedEl.getBox();
40531 var box = this.el.getBox();
40532 if (box.width == 0) {
40533 box.width = this.config.width; // kludge?
40536 box.width += this.split.el.getWidth();
40541 updateBox : function(box){
40542 if(this.split && !this.collapsed){
40543 var sw = this.split.el.getWidth();
40545 this.split.el.setLeft(box.x+box.width);
40546 this.split.el.setTop(box.y);
40547 this.split.el.setHeight(box.height);
40549 if(this.collapsed){
40550 this.updateBody(null, box.height);
40552 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40554 });Roo.namespace("Roo.bootstrap.panel");/*
40556 * Ext JS Library 1.1.1
40557 * Copyright(c) 2006-2007, Ext JS, LLC.
40559 * Originally Released Under LGPL - original licence link has changed is not relivant.
40562 * <script type="text/javascript">
40565 * @class Roo.ContentPanel
40566 * @extends Roo.util.Observable
40568 * A basic ContentPanel element.
40569 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40570 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40571 * @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
40572 * @cfg {Boolean} closable True if the panel can be closed/removed
40573 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40574 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40575 * @cfg {Toolbar} toolbar A toolbar for this panel
40576 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40577 * @cfg {String} title The title for this panel
40578 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40579 * @cfg {String} url Calls {@link #setUrl} with this value
40580 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40581 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40582 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40583 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40584 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40585 * @cfg {Boolean} badges render the badges
40586 * @cfg {String} cls extra classes to use
40587 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40590 * Create a new ContentPanel.
40591 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40592 * @param {String/Object} config A string to set only the title or a config object
40593 * @param {String} content (optional) Set the HTML content for this panel
40594 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40596 Roo.bootstrap.panel.Content = function( config){
40598 this.tpl = config.tpl || false;
40600 var el = config.el;
40601 var content = config.content;
40603 if(config.autoCreate){ // xtype is available if this is called from factory
40606 this.el = Roo.get(el);
40607 if(!this.el && config && config.autoCreate){
40608 if(typeof config.autoCreate == "object"){
40609 if(!config.autoCreate.id){
40610 config.autoCreate.id = config.id||el;
40612 this.el = Roo.DomHelper.append(document.body,
40613 config.autoCreate, true);
40617 cls: (config.cls || '') +
40618 (config.background ? ' bg-' + config.background : '') +
40619 " roo-layout-inactive-content",
40622 if (config.iframe) {
40626 style : 'border: 0px',
40627 src : 'about:blank'
40633 elcfg.html = config.html;
40637 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40638 if (config.iframe) {
40639 this.iframeEl = this.el.select('iframe',true).first();
40644 this.closable = false;
40645 this.loaded = false;
40646 this.active = false;
40649 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40651 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40653 this.wrapEl = this.el; //this.el.wrap();
40655 if (config.toolbar.items) {
40656 ti = config.toolbar.items ;
40657 delete config.toolbar.items ;
40661 this.toolbar.render(this.wrapEl, 'before');
40662 for(var i =0;i < ti.length;i++) {
40663 // Roo.log(['add child', items[i]]);
40664 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40666 this.toolbar.items = nitems;
40667 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40668 delete config.toolbar;
40672 // xtype created footer. - not sure if will work as we normally have to render first..
40673 if (this.footer && !this.footer.el && this.footer.xtype) {
40674 if (!this.wrapEl) {
40675 this.wrapEl = this.el.wrap();
40678 this.footer.container = this.wrapEl.createChild();
40680 this.footer = Roo.factory(this.footer, Roo);
40685 if(typeof config == "string"){
40686 this.title = config;
40688 Roo.apply(this, config);
40692 this.resizeEl = Roo.get(this.resizeEl, true);
40694 this.resizeEl = this.el;
40696 // handle view.xtype
40704 * Fires when this panel is activated.
40705 * @param {Roo.ContentPanel} this
40709 * @event deactivate
40710 * Fires when this panel is activated.
40711 * @param {Roo.ContentPanel} this
40713 "deactivate" : true,
40717 * Fires when this panel is resized if fitToFrame is true.
40718 * @param {Roo.ContentPanel} this
40719 * @param {Number} width The width after any component adjustments
40720 * @param {Number} height The height after any component adjustments
40726 * Fires when this tab is created
40727 * @param {Roo.ContentPanel} this
40733 * Fires when this content is scrolled
40734 * @param {Roo.ContentPanel} this
40735 * @param {Event} scrollEvent
40746 if(this.autoScroll && !this.iframe){
40747 this.resizeEl.setStyle("overflow", "auto");
40748 this.resizeEl.on('scroll', this.onScroll, this);
40750 // fix randome scrolling
40751 //this.el.on('scroll', function() {
40752 // Roo.log('fix random scolling');
40753 // this.scrollTo('top',0);
40756 content = content || this.content;
40758 this.setContent(content);
40760 if(config && config.url){
40761 this.setUrl(this.url, this.params, this.loadOnce);
40766 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40768 if (this.view && typeof(this.view.xtype) != 'undefined') {
40769 this.view.el = this.el.appendChild(document.createElement("div"));
40770 this.view = Roo.factory(this.view);
40771 this.view.render && this.view.render(false, '');
40775 this.fireEvent('render', this);
40778 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40788 /* Resize Element - use this to work out scroll etc. */
40791 setRegion : function(region){
40792 this.region = region;
40793 this.setActiveClass(region && !this.background);
40797 setActiveClass: function(state)
40800 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40801 this.el.setStyle('position','relative');
40803 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40804 this.el.setStyle('position', 'absolute');
40809 * Returns the toolbar for this Panel if one was configured.
40810 * @return {Roo.Toolbar}
40812 getToolbar : function(){
40813 return this.toolbar;
40816 setActiveState : function(active)
40818 this.active = active;
40819 this.setActiveClass(active);
40821 if(this.fireEvent("deactivate", this) === false){
40826 this.fireEvent("activate", this);
40830 * Updates this panel's element (not for iframe)
40831 * @param {String} content The new content
40832 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40834 setContent : function(content, loadScripts){
40839 this.el.update(content, loadScripts);
40842 ignoreResize : function(w, h){
40843 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40846 this.lastSize = {width: w, height: h};
40851 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40852 * @return {Roo.UpdateManager} The UpdateManager
40854 getUpdateManager : function(){
40858 return this.el.getUpdateManager();
40861 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40862 * Does not work with IFRAME contents
40863 * @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:
40866 url: "your-url.php",
40867 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40868 callback: yourFunction,
40869 scope: yourObject, //(optional scope)
40872 text: "Loading...",
40878 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40879 * 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.
40880 * @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}
40881 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40882 * @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.
40883 * @return {Roo.ContentPanel} this
40891 var um = this.el.getUpdateManager();
40892 um.update.apply(um, arguments);
40898 * 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.
40899 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40900 * @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)
40901 * @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)
40902 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40904 setUrl : function(url, params, loadOnce){
40906 this.iframeEl.dom.src = url;
40910 if(this.refreshDelegate){
40911 this.removeListener("activate", this.refreshDelegate);
40913 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40914 this.on("activate", this.refreshDelegate);
40915 return this.el.getUpdateManager();
40918 _handleRefresh : function(url, params, loadOnce){
40919 if(!loadOnce || !this.loaded){
40920 var updater = this.el.getUpdateManager();
40921 updater.update(url, params, this._setLoaded.createDelegate(this));
40925 _setLoaded : function(){
40926 this.loaded = true;
40930 * Returns this panel's id
40933 getId : function(){
40938 * Returns this panel's element - used by regiosn to add.
40939 * @return {Roo.Element}
40941 getEl : function(){
40942 return this.wrapEl || this.el;
40947 adjustForComponents : function(width, height)
40949 //Roo.log('adjustForComponents ');
40950 if(this.resizeEl != this.el){
40951 width -= this.el.getFrameWidth('lr');
40952 height -= this.el.getFrameWidth('tb');
40955 var te = this.toolbar.getEl();
40956 te.setWidth(width);
40957 height -= te.getHeight();
40960 var te = this.footer.getEl();
40961 te.setWidth(width);
40962 height -= te.getHeight();
40966 if(this.adjustments){
40967 width += this.adjustments[0];
40968 height += this.adjustments[1];
40970 return {"width": width, "height": height};
40973 setSize : function(width, height){
40974 if(this.fitToFrame && !this.ignoreResize(width, height)){
40975 if(this.fitContainer && this.resizeEl != this.el){
40976 this.el.setSize(width, height);
40978 var size = this.adjustForComponents(width, height);
40980 this.iframeEl.setSize(width,height);
40983 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40984 this.fireEvent('resize', this, size.width, size.height);
40991 * Returns this panel's title
40994 getTitle : function(){
40996 if (typeof(this.title) != 'object') {
41001 for (var k in this.title) {
41002 if (!this.title.hasOwnProperty(k)) {
41006 if (k.indexOf('-') >= 0) {
41007 var s = k.split('-');
41008 for (var i = 0; i<s.length; i++) {
41009 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
41012 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
41019 * Set this panel's title
41020 * @param {String} title
41022 setTitle : function(title){
41023 this.title = title;
41025 this.region.updatePanelTitle(this, title);
41030 * Returns true is this panel was configured to be closable
41031 * @return {Boolean}
41033 isClosable : function(){
41034 return this.closable;
41037 beforeSlide : function(){
41039 this.resizeEl.clip();
41042 afterSlide : function(){
41044 this.resizeEl.unclip();
41048 * Force a content refresh from the URL specified in the {@link #setUrl} method.
41049 * Will fail silently if the {@link #setUrl} method has not been called.
41050 * This does not activate the panel, just updates its content.
41052 refresh : function(){
41053 if(this.refreshDelegate){
41054 this.loaded = false;
41055 this.refreshDelegate();
41060 * Destroys this panel
41062 destroy : function(){
41063 this.el.removeAllListeners();
41064 var tempEl = document.createElement("span");
41065 tempEl.appendChild(this.el.dom);
41066 tempEl.innerHTML = "";
41072 * form - if the content panel contains a form - this is a reference to it.
41073 * @type {Roo.form.Form}
41077 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41078 * This contains a reference to it.
41084 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41094 * @param {Object} cfg Xtype definition of item to add.
41098 getChildContainer: function () {
41099 return this.getEl();
41103 onScroll : function(e)
41105 this.fireEvent('scroll', this, e);
41110 var ret = new Roo.factory(cfg);
41115 if (cfg.xtype.match(/^Form$/)) {
41118 //if (this.footer) {
41119 // el = this.footer.container.insertSibling(false, 'before');
41121 el = this.el.createChild();
41124 this.form = new Roo.form.Form(cfg);
41127 if ( this.form.allItems.length) {
41128 this.form.render(el.dom);
41132 // should only have one of theses..
41133 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41134 // views.. should not be just added - used named prop 'view''
41136 cfg.el = this.el.appendChild(document.createElement("div"));
41139 var ret = new Roo.factory(cfg);
41141 ret.render && ret.render(false, ''); // render blank..
41151 * @class Roo.bootstrap.panel.Grid
41152 * @extends Roo.bootstrap.panel.Content
41154 * Create a new GridPanel.
41155 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41156 * @param {Object} config A the config object
41162 Roo.bootstrap.panel.Grid = function(config)
41166 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41167 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41169 config.el = this.wrapper;
41170 //this.el = this.wrapper;
41172 if (config.container) {
41173 // ctor'ed from a Border/panel.grid
41176 this.wrapper.setStyle("overflow", "hidden");
41177 this.wrapper.addClass('roo-grid-container');
41182 if(config.toolbar){
41183 var tool_el = this.wrapper.createChild();
41184 this.toolbar = Roo.factory(config.toolbar);
41186 if (config.toolbar.items) {
41187 ti = config.toolbar.items ;
41188 delete config.toolbar.items ;
41192 this.toolbar.render(tool_el);
41193 for(var i =0;i < ti.length;i++) {
41194 // Roo.log(['add child', items[i]]);
41195 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41197 this.toolbar.items = nitems;
41199 delete config.toolbar;
41202 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41203 config.grid.scrollBody = true;;
41204 config.grid.monitorWindowResize = false; // turn off autosizing
41205 config.grid.autoHeight = false;
41206 config.grid.autoWidth = false;
41208 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41210 if (config.background) {
41211 // render grid on panel activation (if panel background)
41212 this.on('activate', function(gp) {
41213 if (!gp.grid.rendered) {
41214 gp.grid.render(this.wrapper);
41215 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41220 this.grid.render(this.wrapper);
41221 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41224 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41225 // ??? needed ??? config.el = this.wrapper;
41230 // xtype created footer. - not sure if will work as we normally have to render first..
41231 if (this.footer && !this.footer.el && this.footer.xtype) {
41233 var ctr = this.grid.getView().getFooterPanel(true);
41234 this.footer.dataSource = this.grid.dataSource;
41235 this.footer = Roo.factory(this.footer, Roo);
41236 this.footer.render(ctr);
41246 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41247 getId : function(){
41248 return this.grid.id;
41252 * Returns the grid for this panel
41253 * @return {Roo.bootstrap.Table}
41255 getGrid : function(){
41259 setSize : function(width, height){
41260 if(!this.ignoreResize(width, height)){
41261 var grid = this.grid;
41262 var size = this.adjustForComponents(width, height);
41263 // tfoot is not a footer?
41266 var gridel = grid.getGridEl();
41267 gridel.setSize(size.width, size.height);
41269 var tbd = grid.getGridEl().select('tbody', true).first();
41270 var thd = grid.getGridEl().select('thead',true).first();
41271 var tbf= grid.getGridEl().select('tfoot', true).first();
41274 size.height -= tbf.getHeight();
41277 size.height -= thd.getHeight();
41280 tbd.setSize(size.width, size.height );
41281 // this is for the account management tab -seems to work there.
41282 var thd = grid.getGridEl().select('thead',true).first();
41284 // tbd.setSize(size.width, size.height - thd.getHeight());
41293 beforeSlide : function(){
41294 this.grid.getView().scroller.clip();
41297 afterSlide : function(){
41298 this.grid.getView().scroller.unclip();
41301 destroy : function(){
41302 this.grid.destroy();
41304 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
41309 * @class Roo.bootstrap.panel.Nest
41310 * @extends Roo.bootstrap.panel.Content
41312 * Create a new Panel, that can contain a layout.Border.
41315 * @param {Roo.BorderLayout} layout The layout for this panel
41316 * @param {String/Object} config A string to set only the title or a config object
41318 Roo.bootstrap.panel.Nest = function(config)
41320 // construct with only one argument..
41321 /* FIXME - implement nicer consturctors
41322 if (layout.layout) {
41324 layout = config.layout;
41325 delete config.layout;
41327 if (layout.xtype && !layout.getEl) {
41328 // then layout needs constructing..
41329 layout = Roo.factory(layout, Roo);
41333 config.el = config.layout.getEl();
41335 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41337 config.layout.monitorWindowResize = false; // turn off autosizing
41338 this.layout = config.layout;
41339 this.layout.getEl().addClass("roo-layout-nested-layout");
41340 this.layout.parent = this;
41347 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41349 setSize : function(width, height){
41350 if(!this.ignoreResize(width, height)){
41351 var size = this.adjustForComponents(width, height);
41352 var el = this.layout.getEl();
41353 if (size.height < 1) {
41354 el.setWidth(size.width);
41356 el.setSize(size.width, size.height);
41358 var touch = el.dom.offsetWidth;
41359 this.layout.layout();
41360 // ie requires a double layout on the first pass
41361 if(Roo.isIE && !this.initialized){
41362 this.initialized = true;
41363 this.layout.layout();
41368 // activate all subpanels if not currently active..
41370 setActiveState : function(active){
41371 this.active = active;
41372 this.setActiveClass(active);
41375 this.fireEvent("deactivate", this);
41379 this.fireEvent("activate", this);
41380 // not sure if this should happen before or after..
41381 if (!this.layout) {
41382 return; // should not happen..
41385 for (var r in this.layout.regions) {
41386 reg = this.layout.getRegion(r);
41387 if (reg.getActivePanel()) {
41388 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41389 reg.setActivePanel(reg.getActivePanel());
41392 if (!reg.panels.length) {
41395 reg.showPanel(reg.getPanel(0));
41404 * Returns the nested BorderLayout for this panel
41405 * @return {Roo.BorderLayout}
41407 getLayout : function(){
41408 return this.layout;
41412 * Adds a xtype elements to the layout of the nested panel
41416 xtype : 'ContentPanel',
41423 xtype : 'NestedLayoutPanel',
41429 items : [ ... list of content panels or nested layout panels.. ]
41433 * @param {Object} cfg Xtype definition of item to add.
41435 addxtype : function(cfg) {
41436 return this.layout.addxtype(cfg);
41441 * Ext JS Library 1.1.1
41442 * Copyright(c) 2006-2007, Ext JS, LLC.
41444 * Originally Released Under LGPL - original licence link has changed is not relivant.
41447 * <script type="text/javascript">
41450 * @class Roo.TabPanel
41451 * @extends Roo.util.Observable
41452 * A lightweight tab container.
41456 // basic tabs 1, built from existing content
41457 var tabs = new Roo.TabPanel("tabs1");
41458 tabs.addTab("script", "View Script");
41459 tabs.addTab("markup", "View Markup");
41460 tabs.activate("script");
41462 // more advanced tabs, built from javascript
41463 var jtabs = new Roo.TabPanel("jtabs");
41464 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41466 // set up the UpdateManager
41467 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41468 var updater = tab2.getUpdateManager();
41469 updater.setDefaultUrl("ajax1.htm");
41470 tab2.on('activate', updater.refresh, updater, true);
41472 // Use setUrl for Ajax loading
41473 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41474 tab3.setUrl("ajax2.htm", null, true);
41477 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41480 jtabs.activate("jtabs-1");
41483 * Create a new TabPanel.
41484 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41485 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41487 Roo.bootstrap.panel.Tabs = function(config){
41489 * The container element for this TabPanel.
41490 * @type Roo.Element
41492 this.el = Roo.get(config.el);
41495 if(typeof config == "boolean"){
41496 this.tabPosition = config ? "bottom" : "top";
41498 Roo.apply(this, config);
41502 if(this.tabPosition == "bottom"){
41503 // if tabs are at the bottom = create the body first.
41504 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41505 this.el.addClass("roo-tabs-bottom");
41507 // next create the tabs holders
41509 if (this.tabPosition == "west"){
41511 var reg = this.region; // fake it..
41513 if (!reg.mgr.parent) {
41516 reg = reg.mgr.parent.region;
41518 Roo.log("got nest?");
41520 if (reg.mgr.getRegion('west')) {
41521 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41522 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41523 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41524 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41525 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41533 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41534 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41535 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41536 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41541 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41544 // finally - if tabs are at the top, then create the body last..
41545 if(this.tabPosition != "bottom"){
41546 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41547 * @type Roo.Element
41549 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41550 this.el.addClass("roo-tabs-top");
41554 this.bodyEl.setStyle("position", "relative");
41556 this.active = null;
41557 this.activateDelegate = this.activate.createDelegate(this);
41562 * Fires when the active tab changes
41563 * @param {Roo.TabPanel} this
41564 * @param {Roo.TabPanelItem} activePanel The new active tab
41568 * @event beforetabchange
41569 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41570 * @param {Roo.TabPanel} this
41571 * @param {Object} e Set cancel to true on this object to cancel the tab change
41572 * @param {Roo.TabPanelItem} tab The tab being changed to
41574 "beforetabchange" : true
41577 Roo.EventManager.onWindowResize(this.onResize, this);
41578 this.cpad = this.el.getPadding("lr");
41579 this.hiddenCount = 0;
41582 // toolbar on the tabbar support...
41583 if (this.toolbar) {
41584 alert("no toolbar support yet");
41585 this.toolbar = false;
41587 var tcfg = this.toolbar;
41588 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41589 this.toolbar = new Roo.Toolbar(tcfg);
41590 if (Roo.isSafari) {
41591 var tbl = tcfg.container.child('table', true);
41592 tbl.setAttribute('width', '100%');
41600 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41603 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41605 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41607 tabPosition : "top",
41609 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41611 currentTabWidth : 0,
41613 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41617 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41621 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41623 preferredTabWidth : 175,
41625 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41627 resizeTabs : false,
41629 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41631 monitorResize : true,
41633 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41635 toolbar : false, // set by caller..
41637 region : false, /// set by caller
41639 disableTooltips : true, // not used yet...
41642 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41643 * @param {String} id The id of the div to use <b>or create</b>
41644 * @param {String} text The text for the tab
41645 * @param {String} content (optional) Content to put in the TabPanelItem body
41646 * @param {Boolean} closable (optional) True to create a close icon on the tab
41647 * @return {Roo.TabPanelItem} The created TabPanelItem
41649 addTab : function(id, text, content, closable, tpl)
41651 var item = new Roo.bootstrap.panel.TabItem({
41655 closable : closable,
41658 this.addTabItem(item);
41660 item.setContent(content);
41666 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41667 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41668 * @return {Roo.TabPanelItem}
41670 getTab : function(id){
41671 return this.items[id];
41675 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41676 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41678 hideTab : function(id){
41679 var t = this.items[id];
41682 this.hiddenCount++;
41683 this.autoSizeTabs();
41688 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41689 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41691 unhideTab : function(id){
41692 var t = this.items[id];
41694 t.setHidden(false);
41695 this.hiddenCount--;
41696 this.autoSizeTabs();
41701 * Adds an existing {@link Roo.TabPanelItem}.
41702 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41704 addTabItem : function(item)
41706 this.items[item.id] = item;
41707 this.items.push(item);
41708 this.autoSizeTabs();
41709 // if(this.resizeTabs){
41710 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41711 // this.autoSizeTabs();
41713 // item.autoSize();
41718 * Removes a {@link Roo.TabPanelItem}.
41719 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41721 removeTab : function(id){
41722 var items = this.items;
41723 var tab = items[id];
41724 if(!tab) { return; }
41725 var index = items.indexOf(tab);
41726 if(this.active == tab && items.length > 1){
41727 var newTab = this.getNextAvailable(index);
41732 this.stripEl.dom.removeChild(tab.pnode.dom);
41733 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41734 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41736 items.splice(index, 1);
41737 delete this.items[tab.id];
41738 tab.fireEvent("close", tab);
41739 tab.purgeListeners();
41740 this.autoSizeTabs();
41743 getNextAvailable : function(start){
41744 var items = this.items;
41746 // look for a next tab that will slide over to
41747 // replace the one being removed
41748 while(index < items.length){
41749 var item = items[++index];
41750 if(item && !item.isHidden()){
41754 // if one isn't found select the previous tab (on the left)
41757 var item = items[--index];
41758 if(item && !item.isHidden()){
41766 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41767 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41769 disableTab : function(id){
41770 var tab = this.items[id];
41771 if(tab && this.active != tab){
41777 * Enables a {@link Roo.TabPanelItem} that is disabled.
41778 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41780 enableTab : function(id){
41781 var tab = this.items[id];
41786 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41787 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41788 * @return {Roo.TabPanelItem} The TabPanelItem.
41790 activate : function(id)
41792 //Roo.log('activite:' + id);
41794 var tab = this.items[id];
41798 if(tab == this.active || tab.disabled){
41802 this.fireEvent("beforetabchange", this, e, tab);
41803 if(e.cancel !== true && !tab.disabled){
41805 this.active.hide();
41807 this.active = this.items[id];
41808 this.active.show();
41809 this.fireEvent("tabchange", this, this.active);
41815 * Gets the active {@link Roo.TabPanelItem}.
41816 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41818 getActiveTab : function(){
41819 return this.active;
41823 * Updates the tab body element to fit the height of the container element
41824 * for overflow scrolling
41825 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41827 syncHeight : function(targetHeight){
41828 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41829 var bm = this.bodyEl.getMargins();
41830 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41831 this.bodyEl.setHeight(newHeight);
41835 onResize : function(){
41836 if(this.monitorResize){
41837 this.autoSizeTabs();
41842 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41844 beginUpdate : function(){
41845 this.updating = true;
41849 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41851 endUpdate : function(){
41852 this.updating = false;
41853 this.autoSizeTabs();
41857 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41859 autoSizeTabs : function()
41861 var count = this.items.length;
41862 var vcount = count - this.hiddenCount;
41865 this.stripEl.hide();
41867 this.stripEl.show();
41870 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41875 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41876 var availWidth = Math.floor(w / vcount);
41877 var b = this.stripBody;
41878 if(b.getWidth() > w){
41879 var tabs = this.items;
41880 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41881 if(availWidth < this.minTabWidth){
41882 /*if(!this.sleft){ // incomplete scrolling code
41883 this.createScrollButtons();
41886 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41889 if(this.currentTabWidth < this.preferredTabWidth){
41890 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41896 * Returns the number of tabs in this TabPanel.
41899 getCount : function(){
41900 return this.items.length;
41904 * Resizes all the tabs to the passed width
41905 * @param {Number} The new width
41907 setTabWidth : function(width){
41908 this.currentTabWidth = width;
41909 for(var i = 0, len = this.items.length; i < len; i++) {
41910 if(!this.items[i].isHidden()) {
41911 this.items[i].setWidth(width);
41917 * Destroys this TabPanel
41918 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41920 destroy : function(removeEl){
41921 Roo.EventManager.removeResizeListener(this.onResize, this);
41922 for(var i = 0, len = this.items.length; i < len; i++){
41923 this.items[i].purgeListeners();
41925 if(removeEl === true){
41926 this.el.update("");
41931 createStrip : function(container)
41933 var strip = document.createElement("nav");
41934 strip.className = Roo.bootstrap.version == 4 ?
41935 "navbar-light bg-light" :
41936 "navbar navbar-default"; //"x-tabs-wrap";
41937 container.appendChild(strip);
41941 createStripList : function(strip)
41943 // div wrapper for retard IE
41944 // returns the "tr" element.
41945 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41946 //'<div class="x-tabs-strip-wrap">'+
41947 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41948 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41949 return strip.firstChild; //.firstChild.firstChild.firstChild;
41951 createBody : function(container)
41953 var body = document.createElement("div");
41954 Roo.id(body, "tab-body");
41955 //Roo.fly(body).addClass("x-tabs-body");
41956 Roo.fly(body).addClass("tab-content");
41957 container.appendChild(body);
41960 createItemBody :function(bodyEl, id){
41961 var body = Roo.getDom(id);
41963 body = document.createElement("div");
41966 //Roo.fly(body).addClass("x-tabs-item-body");
41967 Roo.fly(body).addClass("tab-pane");
41968 bodyEl.insertBefore(body, bodyEl.firstChild);
41972 createStripElements : function(stripEl, text, closable, tpl)
41974 var td = document.createElement("li"); // was td..
41975 td.className = 'nav-item';
41977 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41980 stripEl.appendChild(td);
41982 td.className = "x-tabs-closable";
41983 if(!this.closeTpl){
41984 this.closeTpl = new Roo.Template(
41985 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41986 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41987 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41990 var el = this.closeTpl.overwrite(td, {"text": text});
41991 var close = el.getElementsByTagName("div")[0];
41992 var inner = el.getElementsByTagName("em")[0];
41993 return {"el": el, "close": close, "inner": inner};
41996 // not sure what this is..
41997 // if(!this.tabTpl){
41998 //this.tabTpl = new Roo.Template(
41999 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
42000 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
42002 // this.tabTpl = new Roo.Template(
42003 // '<a href="#">' +
42004 // '<span unselectable="on"' +
42005 // (this.disableTooltips ? '' : ' title="{text}"') +
42006 // ' >{text}</span></a>'
42012 var template = tpl || this.tabTpl || false;
42015 template = new Roo.Template(
42016 Roo.bootstrap.version == 4 ?
42018 '<a class="nav-link" href="#" unselectable="on"' +
42019 (this.disableTooltips ? '' : ' title="{text}"') +
42022 '<a class="nav-link" href="#">' +
42023 '<span unselectable="on"' +
42024 (this.disableTooltips ? '' : ' title="{text}"') +
42025 ' >{text}</span></a>'
42030 switch (typeof(template)) {
42034 template = new Roo.Template(template);
42040 var el = template.overwrite(td, {"text": text});
42042 var inner = el.getElementsByTagName("span")[0];
42044 return {"el": el, "inner": inner};
42052 * @class Roo.TabPanelItem
42053 * @extends Roo.util.Observable
42054 * Represents an individual item (tab plus body) in a TabPanel.
42055 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42056 * @param {String} id The id of this TabPanelItem
42057 * @param {String} text The text for the tab of this TabPanelItem
42058 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42060 Roo.bootstrap.panel.TabItem = function(config){
42062 * The {@link Roo.TabPanel} this TabPanelItem belongs to
42063 * @type Roo.TabPanel
42065 this.tabPanel = config.panel;
42067 * The id for this TabPanelItem
42070 this.id = config.id;
42072 this.disabled = false;
42074 this.text = config.text;
42076 this.loaded = false;
42077 this.closable = config.closable;
42080 * The body element for this TabPanelItem.
42081 * @type Roo.Element
42083 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42084 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42085 this.bodyEl.setStyle("display", "block");
42086 this.bodyEl.setStyle("zoom", "1");
42087 //this.hideAction();
42089 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42091 this.el = Roo.get(els.el);
42092 this.inner = Roo.get(els.inner, true);
42093 this.textEl = Roo.bootstrap.version == 4 ?
42094 this.el : Roo.get(this.el.dom.firstChild, true);
42096 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42097 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42100 // this.el.on("mousedown", this.onTabMouseDown, this);
42101 this.el.on("click", this.onTabClick, this);
42103 if(config.closable){
42104 var c = Roo.get(els.close, true);
42105 c.dom.title = this.closeText;
42106 c.addClassOnOver("close-over");
42107 c.on("click", this.closeClick, this);
42113 * Fires when this tab becomes the active tab.
42114 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42115 * @param {Roo.TabPanelItem} this
42119 * @event beforeclose
42120 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42121 * @param {Roo.TabPanelItem} this
42122 * @param {Object} e Set cancel to true on this object to cancel the close.
42124 "beforeclose": true,
42127 * Fires when this tab is closed.
42128 * @param {Roo.TabPanelItem} this
42132 * @event deactivate
42133 * Fires when this tab is no longer the active tab.
42134 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42135 * @param {Roo.TabPanelItem} this
42137 "deactivate" : true
42139 this.hidden = false;
42141 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42144 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42146 purgeListeners : function(){
42147 Roo.util.Observable.prototype.purgeListeners.call(this);
42148 this.el.removeAllListeners();
42151 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42154 this.status_node.addClass("active");
42157 this.tabPanel.stripWrap.repaint();
42159 this.fireEvent("activate", this.tabPanel, this);
42163 * Returns true if this tab is the active tab.
42164 * @return {Boolean}
42166 isActive : function(){
42167 return this.tabPanel.getActiveTab() == this;
42171 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42174 this.status_node.removeClass("active");
42176 this.fireEvent("deactivate", this.tabPanel, this);
42179 hideAction : function(){
42180 this.bodyEl.hide();
42181 this.bodyEl.setStyle("position", "absolute");
42182 this.bodyEl.setLeft("-20000px");
42183 this.bodyEl.setTop("-20000px");
42186 showAction : function(){
42187 this.bodyEl.setStyle("position", "relative");
42188 this.bodyEl.setTop("");
42189 this.bodyEl.setLeft("");
42190 this.bodyEl.show();
42194 * Set the tooltip for the tab.
42195 * @param {String} tooltip The tab's tooltip
42197 setTooltip : function(text){
42198 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42199 this.textEl.dom.qtip = text;
42200 this.textEl.dom.removeAttribute('title');
42202 this.textEl.dom.title = text;
42206 onTabClick : function(e){
42207 e.preventDefault();
42208 this.tabPanel.activate(this.id);
42211 onTabMouseDown : function(e){
42212 e.preventDefault();
42213 this.tabPanel.activate(this.id);
42216 getWidth : function(){
42217 return this.inner.getWidth();
42220 setWidth : function(width){
42221 var iwidth = width - this.linode.getPadding("lr");
42222 this.inner.setWidth(iwidth);
42223 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42224 this.linode.setWidth(width);
42228 * Show or hide the tab
42229 * @param {Boolean} hidden True to hide or false to show.
42231 setHidden : function(hidden){
42232 this.hidden = hidden;
42233 this.linode.setStyle("display", hidden ? "none" : "");
42237 * Returns true if this tab is "hidden"
42238 * @return {Boolean}
42240 isHidden : function(){
42241 return this.hidden;
42245 * Returns the text for this tab
42248 getText : function(){
42252 autoSize : function(){
42253 //this.el.beginMeasure();
42254 this.textEl.setWidth(1);
42256 * #2804 [new] Tabs in Roojs
42257 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42259 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42260 //this.el.endMeasure();
42264 * Sets the text for the tab (Note: this also sets the tooltip text)
42265 * @param {String} text The tab's text and tooltip
42267 setText : function(text){
42269 this.textEl.update(text);
42270 this.setTooltip(text);
42271 //if(!this.tabPanel.resizeTabs){
42272 // this.autoSize();
42276 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42278 activate : function(){
42279 this.tabPanel.activate(this.id);
42283 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42285 disable : function(){
42286 if(this.tabPanel.active != this){
42287 this.disabled = true;
42288 this.status_node.addClass("disabled");
42293 * Enables this TabPanelItem if it was previously disabled.
42295 enable : function(){
42296 this.disabled = false;
42297 this.status_node.removeClass("disabled");
42301 * Sets the content for this TabPanelItem.
42302 * @param {String} content The content
42303 * @param {Boolean} loadScripts true to look for and load scripts
42305 setContent : function(content, loadScripts){
42306 this.bodyEl.update(content, loadScripts);
42310 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42311 * @return {Roo.UpdateManager} The UpdateManager
42313 getUpdateManager : function(){
42314 return this.bodyEl.getUpdateManager();
42318 * Set a URL to be used to load the content for this TabPanelItem.
42319 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42320 * @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)
42321 * @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)
42322 * @return {Roo.UpdateManager} The UpdateManager
42324 setUrl : function(url, params, loadOnce){
42325 if(this.refreshDelegate){
42326 this.un('activate', this.refreshDelegate);
42328 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42329 this.on("activate", this.refreshDelegate);
42330 return this.bodyEl.getUpdateManager();
42334 _handleRefresh : function(url, params, loadOnce){
42335 if(!loadOnce || !this.loaded){
42336 var updater = this.bodyEl.getUpdateManager();
42337 updater.update(url, params, this._setLoaded.createDelegate(this));
42342 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42343 * Will fail silently if the setUrl method has not been called.
42344 * This does not activate the panel, just updates its content.
42346 refresh : function(){
42347 if(this.refreshDelegate){
42348 this.loaded = false;
42349 this.refreshDelegate();
42354 _setLoaded : function(){
42355 this.loaded = true;
42359 closeClick : function(e){
42362 this.fireEvent("beforeclose", this, o);
42363 if(o.cancel !== true){
42364 this.tabPanel.removeTab(this.id);
42368 * The text displayed in the tooltip for the close icon.
42371 closeText : "Close this tab"
42374 * This script refer to:
42375 * Title: International Telephone Input
42376 * Author: Jack O'Connor
42377 * Code version: v12.1.12
42378 * Availability: https://github.com/jackocnr/intl-tel-input.git
42381 Roo.bootstrap.PhoneInputData = function() {
42384 "Afghanistan (افغانستان)",
42389 "Albania (Shqipëri)",
42394 "Algeria (الجزائر)",
42419 "Antigua and Barbuda",
42429 "Armenia (Հայաստան)",
42445 "Austria (Österreich)",
42450 "Azerbaijan (Azərbaycan)",
42460 "Bahrain (البحرين)",
42465 "Bangladesh (বাংলাদেশ)",
42475 "Belarus (Беларусь)",
42480 "Belgium (België)",
42510 "Bosnia and Herzegovina (Босна и Херцеговина)",
42525 "British Indian Ocean Territory",
42530 "British Virgin Islands",
42540 "Bulgaria (България)",
42550 "Burundi (Uburundi)",
42555 "Cambodia (កម្ពុជា)",
42560 "Cameroon (Cameroun)",
42569 ["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"]
42572 "Cape Verde (Kabu Verdi)",
42577 "Caribbean Netherlands",
42588 "Central African Republic (République centrafricaine)",
42608 "Christmas Island",
42614 "Cocos (Keeling) Islands",
42625 "Comoros (جزر القمر)",
42630 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42635 "Congo (Republic) (Congo-Brazzaville)",
42655 "Croatia (Hrvatska)",
42676 "Czech Republic (Česká republika)",
42681 "Denmark (Danmark)",
42696 "Dominican Republic (República Dominicana)",
42700 ["809", "829", "849"]
42718 "Equatorial Guinea (Guinea Ecuatorial)",
42738 "Falkland Islands (Islas Malvinas)",
42743 "Faroe Islands (Føroyar)",
42764 "French Guiana (Guyane française)",
42769 "French Polynesia (Polynésie française)",
42784 "Georgia (საქართველო)",
42789 "Germany (Deutschland)",
42809 "Greenland (Kalaallit Nunaat)",
42846 "Guinea-Bissau (Guiné Bissau)",
42871 "Hungary (Magyarország)",
42876 "Iceland (Ísland)",
42896 "Iraq (العراق)",
42912 "Israel (ישראל)",
42939 "Jordan (الأردن)",
42944 "Kazakhstan (Казахстан)",
42965 "Kuwait (الكويت)",
42970 "Kyrgyzstan (Кыргызстан)",
42980 "Latvia (Latvija)",
42985 "Lebanon (لبنان)",
43000 "Libya (ليبيا)",
43010 "Lithuania (Lietuva)",
43025 "Macedonia (FYROM) (Македонија)",
43030 "Madagascar (Madagasikara)",
43060 "Marshall Islands",
43070 "Mauritania (موريتانيا)",
43075 "Mauritius (Moris)",
43096 "Moldova (Republica Moldova)",
43106 "Mongolia (Монгол)",
43111 "Montenegro (Crna Gora)",
43121 "Morocco (المغرب)",
43127 "Mozambique (Moçambique)",
43132 "Myanmar (Burma) (မြန်မာ)",
43137 "Namibia (Namibië)",
43152 "Netherlands (Nederland)",
43157 "New Caledonia (Nouvelle-Calédonie)",
43192 "North Korea (조선 민주주의 인민 공화국)",
43197 "Northern Mariana Islands",
43213 "Pakistan (پاکستان)",
43223 "Palestine (فلسطين)",
43233 "Papua New Guinea",
43275 "Réunion (La Réunion)",
43281 "Romania (România)",
43297 "Saint Barthélemy",
43308 "Saint Kitts and Nevis",
43318 "Saint Martin (Saint-Martin (partie française))",
43324 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43329 "Saint Vincent and the Grenadines",
43344 "São Tomé and Príncipe (São Tomé e Príncipe)",
43349 "Saudi Arabia (المملكة العربية السعودية)",
43354 "Senegal (Sénégal)",
43384 "Slovakia (Slovensko)",
43389 "Slovenia (Slovenija)",
43399 "Somalia (Soomaaliya)",
43409 "South Korea (대한민국)",
43414 "South Sudan (جنوب السودان)",
43424 "Sri Lanka (ශ්රී ලංකාව)",
43429 "Sudan (السودان)",
43439 "Svalbard and Jan Mayen",
43450 "Sweden (Sverige)",
43455 "Switzerland (Schweiz)",
43460 "Syria (سوريا)",
43505 "Trinidad and Tobago",
43510 "Tunisia (تونس)",
43515 "Turkey (Türkiye)",
43525 "Turks and Caicos Islands",
43535 "U.S. Virgin Islands",
43545 "Ukraine (Україна)",
43550 "United Arab Emirates (الإمارات العربية المتحدة)",
43572 "Uzbekistan (Oʻzbekiston)",
43582 "Vatican City (Città del Vaticano)",
43593 "Vietnam (Việt Nam)",
43598 "Wallis and Futuna (Wallis-et-Futuna)",
43603 "Western Sahara (الصحراء الغربية)",
43609 "Yemen (اليمن)",
43633 * This script refer to:
43634 * Title: International Telephone Input
43635 * Author: Jack O'Connor
43636 * Code version: v12.1.12
43637 * Availability: https://github.com/jackocnr/intl-tel-input.git
43641 * @class Roo.bootstrap.PhoneInput
43642 * @extends Roo.bootstrap.TriggerField
43643 * An input with International dial-code selection
43645 * @cfg {String} defaultDialCode default '+852'
43646 * @cfg {Array} preferedCountries default []
43649 * Create a new PhoneInput.
43650 * @param {Object} config Configuration options
43653 Roo.bootstrap.PhoneInput = function(config) {
43654 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43657 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43659 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43661 listWidth: undefined,
43663 selectedClass: 'active',
43665 invalidClass : "has-warning",
43667 validClass: 'has-success',
43669 allowed: '0123456789',
43674 * @cfg {String} defaultDialCode The default dial code when initializing the input
43676 defaultDialCode: '+852',
43679 * @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
43681 preferedCountries: false,
43683 getAutoCreate : function()
43685 var data = Roo.bootstrap.PhoneInputData();
43686 var align = this.labelAlign || this.parentLabelAlign();
43689 this.allCountries = [];
43690 this.dialCodeMapping = [];
43692 for (var i = 0; i < data.length; i++) {
43694 this.allCountries[i] = {
43698 priority: c[3] || 0,
43699 areaCodes: c[4] || null
43701 this.dialCodeMapping[c[2]] = {
43704 priority: c[3] || 0,
43705 areaCodes: c[4] || null
43717 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43718 maxlength: this.max_length,
43719 cls : 'form-control tel-input',
43720 autocomplete: 'new-password'
43723 var hiddenInput = {
43726 cls: 'hidden-tel-input'
43730 hiddenInput.name = this.name;
43733 if (this.disabled) {
43734 input.disabled = true;
43737 var flag_container = {
43754 cls: this.hasFeedback ? 'has-feedback' : '',
43760 cls: 'dial-code-holder',
43767 cls: 'roo-select2-container input-group',
43774 if (this.fieldLabel.length) {
43777 tooltip: 'This field is required'
43783 cls: 'control-label',
43789 html: this.fieldLabel
43792 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43798 if(this.indicatorpos == 'right') {
43799 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43806 if(align == 'left') {
43814 if(this.labelWidth > 12){
43815 label.style = "width: " + this.labelWidth + 'px';
43817 if(this.labelWidth < 13 && this.labelmd == 0){
43818 this.labelmd = this.labelWidth;
43820 if(this.labellg > 0){
43821 label.cls += ' col-lg-' + this.labellg;
43822 input.cls += ' col-lg-' + (12 - this.labellg);
43824 if(this.labelmd > 0){
43825 label.cls += ' col-md-' + this.labelmd;
43826 container.cls += ' col-md-' + (12 - this.labelmd);
43828 if(this.labelsm > 0){
43829 label.cls += ' col-sm-' + this.labelsm;
43830 container.cls += ' col-sm-' + (12 - this.labelsm);
43832 if(this.labelxs > 0){
43833 label.cls += ' col-xs-' + this.labelxs;
43834 container.cls += ' col-xs-' + (12 - this.labelxs);
43844 var settings = this;
43846 ['xs','sm','md','lg'].map(function(size){
43847 if (settings[size]) {
43848 cfg.cls += ' col-' + size + '-' + settings[size];
43852 this.store = new Roo.data.Store({
43853 proxy : new Roo.data.MemoryProxy({}),
43854 reader : new Roo.data.JsonReader({
43865 'name' : 'dialCode',
43869 'name' : 'priority',
43873 'name' : 'areaCodes',
43880 if(!this.preferedCountries) {
43881 this.preferedCountries = [
43888 var p = this.preferedCountries.reverse();
43891 for (var i = 0; i < p.length; i++) {
43892 for (var j = 0; j < this.allCountries.length; j++) {
43893 if(this.allCountries[j].iso2 == p[i]) {
43894 var t = this.allCountries[j];
43895 this.allCountries.splice(j,1);
43896 this.allCountries.unshift(t);
43902 this.store.proxy.data = {
43904 data: this.allCountries
43910 initEvents : function()
43913 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43915 this.indicator = this.indicatorEl();
43916 this.flag = this.flagEl();
43917 this.dialCodeHolder = this.dialCodeHolderEl();
43919 this.trigger = this.el.select('div.flag-box',true).first();
43920 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43925 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43926 _this.list.setWidth(lw);
43929 this.list.on('mouseover', this.onViewOver, this);
43930 this.list.on('mousemove', this.onViewMove, this);
43931 this.inputEl().on("keyup", this.onKeyUp, this);
43932 this.inputEl().on("keypress", this.onKeyPress, this);
43934 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43936 this.view = new Roo.View(this.list, this.tpl, {
43937 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43940 this.view.on('click', this.onViewClick, this);
43941 this.setValue(this.defaultDialCode);
43944 onTriggerClick : function(e)
43946 Roo.log('trigger click');
43951 if(this.isExpanded()){
43953 this.hasFocus = false;
43955 this.store.load({});
43956 this.hasFocus = true;
43961 isExpanded : function()
43963 return this.list.isVisible();
43966 collapse : function()
43968 if(!this.isExpanded()){
43972 Roo.get(document).un('mousedown', this.collapseIf, this);
43973 Roo.get(document).un('mousewheel', this.collapseIf, this);
43974 this.fireEvent('collapse', this);
43978 expand : function()
43982 if(this.isExpanded() || !this.hasFocus){
43986 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43987 this.list.setWidth(lw);
43990 this.restrictHeight();
43992 Roo.get(document).on('mousedown', this.collapseIf, this);
43993 Roo.get(document).on('mousewheel', this.collapseIf, this);
43995 this.fireEvent('expand', this);
43998 restrictHeight : function()
44000 this.list.alignTo(this.inputEl(), this.listAlign);
44001 this.list.alignTo(this.inputEl(), this.listAlign);
44004 onViewOver : function(e, t)
44006 if(this.inKeyMode){
44009 var item = this.view.findItemFromChild(t);
44012 var index = this.view.indexOf(item);
44013 this.select(index, false);
44018 onViewClick : function(view, doFocus, el, e)
44020 var index = this.view.getSelectedIndexes()[0];
44022 var r = this.store.getAt(index);
44025 this.onSelect(r, index);
44027 if(doFocus !== false && !this.blockFocus){
44028 this.inputEl().focus();
44032 onViewMove : function(e, t)
44034 this.inKeyMode = false;
44037 select : function(index, scrollIntoView)
44039 this.selectedIndex = index;
44040 this.view.select(index);
44041 if(scrollIntoView !== false){
44042 var el = this.view.getNode(index);
44044 this.list.scrollChildIntoView(el, false);
44049 createList : function()
44051 this.list = Roo.get(document.body).createChild({
44053 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44054 style: 'display:none'
44057 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44060 collapseIf : function(e)
44062 var in_combo = e.within(this.el);
44063 var in_list = e.within(this.list);
44064 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44066 if (in_combo || in_list || is_list) {
44072 onSelect : function(record, index)
44074 if(this.fireEvent('beforeselect', this, record, index) !== false){
44076 this.setFlagClass(record.data.iso2);
44077 this.setDialCode(record.data.dialCode);
44078 this.hasFocus = false;
44080 this.fireEvent('select', this, record, index);
44084 flagEl : function()
44086 var flag = this.el.select('div.flag',true).first();
44093 dialCodeHolderEl : function()
44095 var d = this.el.select('input.dial-code-holder',true).first();
44102 setDialCode : function(v)
44104 this.dialCodeHolder.dom.value = '+'+v;
44107 setFlagClass : function(n)
44109 this.flag.dom.className = 'flag '+n;
44112 getValue : function()
44114 var v = this.inputEl().getValue();
44115 if(this.dialCodeHolder) {
44116 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44121 setValue : function(v)
44123 var d = this.getDialCode(v);
44125 //invalid dial code
44126 if(v.length == 0 || !d || d.length == 0) {
44128 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44129 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44135 this.setFlagClass(this.dialCodeMapping[d].iso2);
44136 this.setDialCode(d);
44137 this.inputEl().dom.value = v.replace('+'+d,'');
44138 this.hiddenEl().dom.value = this.getValue();
44143 getDialCode : function(v)
44147 if (v.length == 0) {
44148 return this.dialCodeHolder.dom.value;
44152 if (v.charAt(0) != "+") {
44155 var numericChars = "";
44156 for (var i = 1; i < v.length; i++) {
44157 var c = v.charAt(i);
44160 if (this.dialCodeMapping[numericChars]) {
44161 dialCode = v.substr(1, i);
44163 if (numericChars.length == 4) {
44173 this.setValue(this.defaultDialCode);
44177 hiddenEl : function()
44179 return this.el.select('input.hidden-tel-input',true).first();
44182 // after setting val
44183 onKeyUp : function(e){
44184 this.setValue(this.getValue());
44187 onKeyPress : function(e){
44188 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44195 * @class Roo.bootstrap.MoneyField
44196 * @extends Roo.bootstrap.ComboBox
44197 * Bootstrap MoneyField class
44200 * Create a new MoneyField.
44201 * @param {Object} config Configuration options
44204 Roo.bootstrap.MoneyField = function(config) {
44206 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44210 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44213 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44215 allowDecimals : true,
44217 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44219 decimalSeparator : ".",
44221 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44223 decimalPrecision : 0,
44225 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44227 allowNegative : true,
44229 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44233 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44235 minValue : Number.NEGATIVE_INFINITY,
44237 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44239 maxValue : Number.MAX_VALUE,
44241 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44243 minText : "The minimum value for this field is {0}",
44245 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44247 maxText : "The maximum value for this field is {0}",
44249 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
44250 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44252 nanText : "{0} is not a valid number",
44254 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44258 * @cfg {String} defaults currency of the MoneyField
44259 * value should be in lkey
44261 defaultCurrency : false,
44263 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44265 thousandsDelimiter : false,
44267 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44278 getAutoCreate : function()
44280 var align = this.labelAlign || this.parentLabelAlign();
44292 cls : 'form-control roo-money-amount-input',
44293 autocomplete: 'new-password'
44296 var hiddenInput = {
44300 cls: 'hidden-number-input'
44303 if(this.max_length) {
44304 input.maxlength = this.max_length;
44308 hiddenInput.name = this.name;
44311 if (this.disabled) {
44312 input.disabled = true;
44315 var clg = 12 - this.inputlg;
44316 var cmd = 12 - this.inputmd;
44317 var csm = 12 - this.inputsm;
44318 var cxs = 12 - this.inputxs;
44322 cls : 'row roo-money-field',
44326 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44330 cls: 'roo-select2-container input-group',
44334 cls : 'form-control roo-money-currency-input',
44335 autocomplete: 'new-password',
44337 name : this.currencyName
44341 cls : 'input-group-addon',
44355 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44359 cls: this.hasFeedback ? 'has-feedback' : '',
44370 if (this.fieldLabel.length) {
44373 tooltip: 'This field is required'
44379 cls: 'control-label',
44385 html: this.fieldLabel
44388 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44394 if(this.indicatorpos == 'right') {
44395 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44402 if(align == 'left') {
44410 if(this.labelWidth > 12){
44411 label.style = "width: " + this.labelWidth + 'px';
44413 if(this.labelWidth < 13 && this.labelmd == 0){
44414 this.labelmd = this.labelWidth;
44416 if(this.labellg > 0){
44417 label.cls += ' col-lg-' + this.labellg;
44418 input.cls += ' col-lg-' + (12 - this.labellg);
44420 if(this.labelmd > 0){
44421 label.cls += ' col-md-' + this.labelmd;
44422 container.cls += ' col-md-' + (12 - this.labelmd);
44424 if(this.labelsm > 0){
44425 label.cls += ' col-sm-' + this.labelsm;
44426 container.cls += ' col-sm-' + (12 - this.labelsm);
44428 if(this.labelxs > 0){
44429 label.cls += ' col-xs-' + this.labelxs;
44430 container.cls += ' col-xs-' + (12 - this.labelxs);
44441 var settings = this;
44443 ['xs','sm','md','lg'].map(function(size){
44444 if (settings[size]) {
44445 cfg.cls += ' col-' + size + '-' + settings[size];
44452 initEvents : function()
44454 this.indicator = this.indicatorEl();
44456 this.initCurrencyEvent();
44458 this.initNumberEvent();
44461 initCurrencyEvent : function()
44464 throw "can not find store for combo";
44467 this.store = Roo.factory(this.store, Roo.data);
44468 this.store.parent = this;
44472 this.triggerEl = this.el.select('.input-group-addon', true).first();
44474 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44479 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44480 _this.list.setWidth(lw);
44483 this.list.on('mouseover', this.onViewOver, this);
44484 this.list.on('mousemove', this.onViewMove, this);
44485 this.list.on('scroll', this.onViewScroll, this);
44488 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44491 this.view = new Roo.View(this.list, this.tpl, {
44492 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44495 this.view.on('click', this.onViewClick, this);
44497 this.store.on('beforeload', this.onBeforeLoad, this);
44498 this.store.on('load', this.onLoad, this);
44499 this.store.on('loadexception', this.onLoadException, this);
44501 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44502 "up" : function(e){
44503 this.inKeyMode = true;
44507 "down" : function(e){
44508 if(!this.isExpanded()){
44509 this.onTriggerClick();
44511 this.inKeyMode = true;
44516 "enter" : function(e){
44519 if(this.fireEvent("specialkey", this, e)){
44520 this.onViewClick(false);
44526 "esc" : function(e){
44530 "tab" : function(e){
44533 if(this.fireEvent("specialkey", this, e)){
44534 this.onViewClick(false);
44542 doRelay : function(foo, bar, hname){
44543 if(hname == 'down' || this.scope.isExpanded()){
44544 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44552 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44556 initNumberEvent : function(e)
44558 this.inputEl().on("keydown" , this.fireKey, this);
44559 this.inputEl().on("focus", this.onFocus, this);
44560 this.inputEl().on("blur", this.onBlur, this);
44562 this.inputEl().relayEvent('keyup', this);
44564 if(this.indicator){
44565 this.indicator.addClass('invisible');
44568 this.originalValue = this.getValue();
44570 if(this.validationEvent == 'keyup'){
44571 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44572 this.inputEl().on('keyup', this.filterValidation, this);
44574 else if(this.validationEvent !== false){
44575 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44578 if(this.selectOnFocus){
44579 this.on("focus", this.preFocus, this);
44582 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44583 this.inputEl().on("keypress", this.filterKeys, this);
44585 this.inputEl().relayEvent('keypress', this);
44588 var allowed = "0123456789";
44590 if(this.allowDecimals){
44591 allowed += this.decimalSeparator;
44594 if(this.allowNegative){
44598 if(this.thousandsDelimiter) {
44602 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44604 var keyPress = function(e){
44606 var k = e.getKey();
44608 var c = e.getCharCode();
44611 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44612 allowed.indexOf(String.fromCharCode(c)) === -1
44618 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44622 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44627 this.inputEl().on("keypress", keyPress, this);
44631 onTriggerClick : function(e)
44638 this.loadNext = false;
44640 if(this.isExpanded()){
44645 this.hasFocus = true;
44647 if(this.triggerAction == 'all') {
44648 this.doQuery(this.allQuery, true);
44652 this.doQuery(this.getRawValue());
44655 getCurrency : function()
44657 var v = this.currencyEl().getValue();
44662 restrictHeight : function()
44664 this.list.alignTo(this.currencyEl(), this.listAlign);
44665 this.list.alignTo(this.currencyEl(), this.listAlign);
44668 onViewClick : function(view, doFocus, el, e)
44670 var index = this.view.getSelectedIndexes()[0];
44672 var r = this.store.getAt(index);
44675 this.onSelect(r, index);
44679 onSelect : function(record, index){
44681 if(this.fireEvent('beforeselect', this, record, index) !== false){
44683 this.setFromCurrencyData(index > -1 ? record.data : false);
44687 this.fireEvent('select', this, record, index);
44691 setFromCurrencyData : function(o)
44695 this.lastCurrency = o;
44697 if (this.currencyField) {
44698 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44700 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44703 this.lastSelectionText = currency;
44705 //setting default currency
44706 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44707 this.setCurrency(this.defaultCurrency);
44711 this.setCurrency(currency);
44714 setFromData : function(o)
44718 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44720 this.setFromCurrencyData(c);
44725 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44727 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44730 this.setValue(value);
44734 setCurrency : function(v)
44736 this.currencyValue = v;
44739 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44744 setValue : function(v)
44746 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44752 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44754 this.inputEl().dom.value = (v == '') ? '' :
44755 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44757 if(!this.allowZero && v === '0') {
44758 this.hiddenEl().dom.value = '';
44759 this.inputEl().dom.value = '';
44766 getRawValue : function()
44768 var v = this.inputEl().getValue();
44773 getValue : function()
44775 return this.fixPrecision(this.parseValue(this.getRawValue()));
44778 parseValue : function(value)
44780 if(this.thousandsDelimiter) {
44782 r = new RegExp(",", "g");
44783 value = value.replace(r, "");
44786 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44787 return isNaN(value) ? '' : value;
44791 fixPrecision : function(value)
44793 if(this.thousandsDelimiter) {
44795 r = new RegExp(",", "g");
44796 value = value.replace(r, "");
44799 var nan = isNaN(value);
44801 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44802 return nan ? '' : value;
44804 return parseFloat(value).toFixed(this.decimalPrecision);
44807 decimalPrecisionFcn : function(v)
44809 return Math.floor(v);
44812 validateValue : function(value)
44814 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44818 var num = this.parseValue(value);
44821 this.markInvalid(String.format(this.nanText, value));
44825 if(num < this.minValue){
44826 this.markInvalid(String.format(this.minText, this.minValue));
44830 if(num > this.maxValue){
44831 this.markInvalid(String.format(this.maxText, this.maxValue));
44838 validate : function()
44840 if(this.disabled || this.allowBlank){
44845 var currency = this.getCurrency();
44847 if(this.validateValue(this.getRawValue()) && currency.length){
44852 this.markInvalid();
44856 getName: function()
44861 beforeBlur : function()
44867 var v = this.parseValue(this.getRawValue());
44874 onBlur : function()
44878 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44879 //this.el.removeClass(this.focusClass);
44882 this.hasFocus = false;
44884 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44888 var v = this.getValue();
44890 if(String(v) !== String(this.startValue)){
44891 this.fireEvent('change', this, v, this.startValue);
44894 this.fireEvent("blur", this);
44897 inputEl : function()
44899 return this.el.select('.roo-money-amount-input', true).first();
44902 currencyEl : function()
44904 return this.el.select('.roo-money-currency-input', true).first();
44907 hiddenEl : function()
44909 return this.el.select('input.hidden-number-input',true).first();
44913 * @class Roo.bootstrap.BezierSignature
44914 * @extends Roo.bootstrap.Component
44915 * Bootstrap BezierSignature class
44916 * This script refer to:
44917 * Title: Signature Pad
44919 * Availability: https://github.com/szimek/signature_pad
44922 * Create a new BezierSignature
44923 * @param {Object} config The config object
44926 Roo.bootstrap.BezierSignature = function(config){
44927 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44933 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44940 mouse_btn_down: true,
44943 * @cfg {int} canvas height
44945 canvas_height: '200px',
44948 * @cfg {float|function} Radius of a single dot.
44953 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44958 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44963 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44968 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44973 * @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.
44975 bg_color: 'rgba(0, 0, 0, 0)',
44978 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44980 dot_color: 'black',
44983 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44985 velocity_filter_weight: 0.7,
44988 * @cfg {function} Callback when stroke begin.
44993 * @cfg {function} Callback when stroke end.
44997 getAutoCreate : function()
44999 var cls = 'roo-signature column';
45002 cls += ' ' + this.cls;
45012 for(var i = 0; i < col_sizes.length; i++) {
45013 if(this[col_sizes[i]]) {
45014 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
45024 cls: 'roo-signature-body',
45028 cls: 'roo-signature-body-canvas',
45029 height: this.canvas_height,
45030 width: this.canvas_width
45037 style: 'display: none'
45045 initEvents: function()
45047 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45049 var canvas = this.canvasEl();
45051 // mouse && touch event swapping...
45052 canvas.dom.style.touchAction = 'none';
45053 canvas.dom.style.msTouchAction = 'none';
45055 this.mouse_btn_down = false;
45056 canvas.on('mousedown', this._handleMouseDown, this);
45057 canvas.on('mousemove', this._handleMouseMove, this);
45058 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45060 if (window.PointerEvent) {
45061 canvas.on('pointerdown', this._handleMouseDown, this);
45062 canvas.on('pointermove', this._handleMouseMove, this);
45063 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45066 if ('ontouchstart' in window) {
45067 canvas.on('touchstart', this._handleTouchStart, this);
45068 canvas.on('touchmove', this._handleTouchMove, this);
45069 canvas.on('touchend', this._handleTouchEnd, this);
45072 Roo.EventManager.onWindowResize(this.resize, this, true);
45074 // file input event
45075 this.fileEl().on('change', this.uploadImage, this);
45082 resize: function(){
45084 var canvas = this.canvasEl().dom;
45085 var ctx = this.canvasElCtx();
45086 var img_data = false;
45088 if(canvas.width > 0) {
45089 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45091 // setting canvas width will clean img data
45094 var style = window.getComputedStyle ?
45095 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45097 var padding_left = parseInt(style.paddingLeft) || 0;
45098 var padding_right = parseInt(style.paddingRight) || 0;
45100 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45103 ctx.putImageData(img_data, 0, 0);
45107 _handleMouseDown: function(e)
45109 if (e.browserEvent.which === 1) {
45110 this.mouse_btn_down = true;
45111 this.strokeBegin(e);
45115 _handleMouseMove: function (e)
45117 if (this.mouse_btn_down) {
45118 this.strokeMoveUpdate(e);
45122 _handleMouseUp: function (e)
45124 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45125 this.mouse_btn_down = false;
45130 _handleTouchStart: function (e) {
45132 e.preventDefault();
45133 if (e.browserEvent.targetTouches.length === 1) {
45134 // var touch = e.browserEvent.changedTouches[0];
45135 // this.strokeBegin(touch);
45137 this.strokeBegin(e); // assume e catching the correct xy...
45141 _handleTouchMove: function (e) {
45142 e.preventDefault();
45143 // var touch = event.targetTouches[0];
45144 // _this._strokeMoveUpdate(touch);
45145 this.strokeMoveUpdate(e);
45148 _handleTouchEnd: function (e) {
45149 var wasCanvasTouched = e.target === this.canvasEl().dom;
45150 if (wasCanvasTouched) {
45151 e.preventDefault();
45152 // var touch = event.changedTouches[0];
45153 // _this._strokeEnd(touch);
45158 reset: function () {
45159 this._lastPoints = [];
45160 this._lastVelocity = 0;
45161 this._lastWidth = (this.min_width + this.max_width) / 2;
45162 this.canvasElCtx().fillStyle = this.dot_color;
45165 strokeMoveUpdate: function(e)
45167 this.strokeUpdate(e);
45169 if (this.throttle) {
45170 this.throttleStroke(this.strokeUpdate, this.throttle);
45173 this.strokeUpdate(e);
45177 strokeBegin: function(e)
45179 var newPointGroup = {
45180 color: this.dot_color,
45184 if (typeof this.onBegin === 'function') {
45188 this.curve_data.push(newPointGroup);
45190 this.strokeUpdate(e);
45193 strokeUpdate: function(e)
45195 var rect = this.canvasEl().dom.getBoundingClientRect();
45196 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45197 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45198 var lastPoints = lastPointGroup.points;
45199 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45200 var isLastPointTooClose = lastPoint
45201 ? point.distanceTo(lastPoint) <= this.min_distance
45203 var color = lastPointGroup.color;
45204 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45205 var curve = this.addPoint(point);
45207 this.drawDot({color: color, point: point});
45210 this.drawCurve({color: color, curve: curve});
45220 strokeEnd: function(e)
45222 this.strokeUpdate(e);
45223 if (typeof this.onEnd === 'function') {
45228 addPoint: function (point) {
45229 var _lastPoints = this._lastPoints;
45230 _lastPoints.push(point);
45231 if (_lastPoints.length > 2) {
45232 if (_lastPoints.length === 3) {
45233 _lastPoints.unshift(_lastPoints[0]);
45235 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45236 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45237 _lastPoints.shift();
45243 calculateCurveWidths: function (startPoint, endPoint) {
45244 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45245 (1 - this.velocity_filter_weight) * this._lastVelocity;
45247 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45250 start: this._lastWidth
45253 this._lastVelocity = velocity;
45254 this._lastWidth = newWidth;
45258 drawDot: function (_a) {
45259 var color = _a.color, point = _a.point;
45260 var ctx = this.canvasElCtx();
45261 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45263 this.drawCurveSegment(point.x, point.y, width);
45265 ctx.fillStyle = color;
45269 drawCurve: function (_a) {
45270 var color = _a.color, curve = _a.curve;
45271 var ctx = this.canvasElCtx();
45272 var widthDelta = curve.endWidth - curve.startWidth;
45273 var drawSteps = Math.floor(curve.length()) * 2;
45275 ctx.fillStyle = color;
45276 for (var i = 0; i < drawSteps; i += 1) {
45277 var t = i / drawSteps;
45283 var x = uuu * curve.startPoint.x;
45284 x += 3 * uu * t * curve.control1.x;
45285 x += 3 * u * tt * curve.control2.x;
45286 x += ttt * curve.endPoint.x;
45287 var y = uuu * curve.startPoint.y;
45288 y += 3 * uu * t * curve.control1.y;
45289 y += 3 * u * tt * curve.control2.y;
45290 y += ttt * curve.endPoint.y;
45291 var width = curve.startWidth + ttt * widthDelta;
45292 this.drawCurveSegment(x, y, width);
45298 drawCurveSegment: function (x, y, width) {
45299 var ctx = this.canvasElCtx();
45301 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45302 this.is_empty = false;
45307 var ctx = this.canvasElCtx();
45308 var canvas = this.canvasEl().dom;
45309 ctx.fillStyle = this.bg_color;
45310 ctx.clearRect(0, 0, canvas.width, canvas.height);
45311 ctx.fillRect(0, 0, canvas.width, canvas.height);
45312 this.curve_data = [];
45314 this.is_empty = true;
45319 return this.el.select('input',true).first();
45322 canvasEl: function()
45324 return this.el.select('canvas',true).first();
45327 canvasElCtx: function()
45329 return this.el.select('canvas',true).first().dom.getContext('2d');
45332 getImage: function(type)
45334 if(this.is_empty) {
45339 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45342 drawFromImage: function(img_src)
45344 var img = new Image();
45346 img.onload = function(){
45347 this.canvasElCtx().drawImage(img, 0, 0);
45352 this.is_empty = false;
45355 selectImage: function()
45357 this.fileEl().dom.click();
45360 uploadImage: function(e)
45362 var reader = new FileReader();
45364 reader.onload = function(e){
45365 var img = new Image();
45366 img.onload = function(){
45368 this.canvasElCtx().drawImage(img, 0, 0);
45370 img.src = e.target.result;
45373 reader.readAsDataURL(e.target.files[0]);
45376 // Bezier Point Constructor
45377 Point: (function () {
45378 function Point(x, y, time) {
45381 this.time = time || Date.now();
45383 Point.prototype.distanceTo = function (start) {
45384 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45386 Point.prototype.equals = function (other) {
45387 return this.x === other.x && this.y === other.y && this.time === other.time;
45389 Point.prototype.velocityFrom = function (start) {
45390 return this.time !== start.time
45391 ? this.distanceTo(start) / (this.time - start.time)
45398 // Bezier Constructor
45399 Bezier: (function () {
45400 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45401 this.startPoint = startPoint;
45402 this.control2 = control2;
45403 this.control1 = control1;
45404 this.endPoint = endPoint;
45405 this.startWidth = startWidth;
45406 this.endWidth = endWidth;
45408 Bezier.fromPoints = function (points, widths, scope) {
45409 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45410 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45411 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45413 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45414 var dx1 = s1.x - s2.x;
45415 var dy1 = s1.y - s2.y;
45416 var dx2 = s2.x - s3.x;
45417 var dy2 = s2.y - s3.y;
45418 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45419 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45420 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45421 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45422 var dxm = m1.x - m2.x;
45423 var dym = m1.y - m2.y;
45424 var k = l2 / (l1 + l2);
45425 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45426 var tx = s2.x - cm.x;
45427 var ty = s2.y - cm.y;
45429 c1: new scope.Point(m1.x + tx, m1.y + ty),
45430 c2: new scope.Point(m2.x + tx, m2.y + ty)
45433 Bezier.prototype.length = function () {
45438 for (var i = 0; i <= steps; i += 1) {
45440 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45441 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45443 var xdiff = cx - px;
45444 var ydiff = cy - py;
45445 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45452 Bezier.prototype.point = function (t, start, c1, c2, end) {
45453 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45454 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45455 + (3.0 * c2 * (1.0 - t) * t * t)
45456 + (end * t * t * t);
45461 throttleStroke: function(fn, wait) {
45462 if (wait === void 0) { wait = 250; }
45464 var timeout = null;
45468 var later = function () {
45469 previous = Date.now();
45471 result = fn.apply(storedContext, storedArgs);
45473 storedContext = null;
45477 return function wrapper() {
45479 for (var _i = 0; _i < arguments.length; _i++) {
45480 args[_i] = arguments[_i];
45482 var now = Date.now();
45483 var remaining = wait - (now - previous);
45484 storedContext = this;
45486 if (remaining <= 0 || remaining > wait) {
45488 clearTimeout(timeout);
45492 result = fn.apply(storedContext, storedArgs);
45494 storedContext = null;
45498 else if (!timeout) {
45499 timeout = window.setTimeout(later, remaining);