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 * Bootstrap Row class (contains columns...)
7104 * @param {Object} config The config object
7107 Roo.bootstrap.Row = function(config){
7108 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7111 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7113 getAutoCreate : function(){
7132 * @class Roo.bootstrap.Pagination
7133 * @extends Roo.bootstrap.Component
7134 * Bootstrap Pagination class
7135 * @cfg {String} size xs | sm | md | lg
7136 * @cfg {Boolean} inverse false | true
7139 * Create a new Pagination
7140 * @param {Object} config The config object
7143 Roo.bootstrap.Pagination = function(config){
7144 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7147 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7153 getAutoCreate : function(){
7159 cfg.cls += ' inverse';
7165 cfg.cls += " " + this.cls;
7183 * @class Roo.bootstrap.PaginationItem
7184 * @extends Roo.bootstrap.Component
7185 * Bootstrap PaginationItem class
7186 * @cfg {String} html text
7187 * @cfg {String} href the link
7188 * @cfg {Boolean} preventDefault (true | false) default true
7189 * @cfg {Boolean} active (true | false) default false
7190 * @cfg {Boolean} disabled default false
7194 * Create a new PaginationItem
7195 * @param {Object} config The config object
7199 Roo.bootstrap.PaginationItem = function(config){
7200 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7205 * The raw click event for the entire grid.
7206 * @param {Roo.EventObject} e
7212 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7216 preventDefault: true,
7221 getAutoCreate : function(){
7227 href : this.href ? this.href : '#',
7228 html : this.html ? this.html : ''
7238 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7242 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7248 initEvents: function() {
7250 this.el.on('click', this.onClick, this);
7253 onClick : function(e)
7255 Roo.log('PaginationItem on click ');
7256 if(this.preventDefault){
7264 this.fireEvent('click', this, e);
7280 * @class Roo.bootstrap.Slider
7281 * @extends Roo.bootstrap.Component
7282 * Bootstrap Slider class
7285 * Create a new Slider
7286 * @param {Object} config The config object
7289 Roo.bootstrap.Slider = function(config){
7290 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7293 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7295 getAutoCreate : function(){
7299 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7303 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7315 * Ext JS Library 1.1.1
7316 * Copyright(c) 2006-2007, Ext JS, LLC.
7318 * Originally Released Under LGPL - original licence link has changed is not relivant.
7321 * <script type="text/javascript">
7324 * @extends Roo.dd.DDProxy
7325 * @class Roo.grid.SplitDragZone
7326 * Support for Column Header resizing
7328 * @param {Object} config
7331 // This is a support class used internally by the Grid components
7332 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7334 this.view = grid.getView();
7335 this.proxy = this.view.resizeProxy;
7336 Roo.grid.SplitDragZone.superclass.constructor.call(
7339 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7341 dragElId : Roo.id(this.proxy.dom),
7346 this.setHandleElId(Roo.id(hd));
7347 if (hd2 !== false) {
7348 this.setOuterHandleElId(Roo.id(hd2));
7351 this.scroll = false;
7353 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7354 fly: Roo.Element.fly,
7356 b4StartDrag : function(x, y){
7357 this.view.headersDisabled = true;
7358 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7359 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7361 this.proxy.setHeight(h);
7363 // for old system colWidth really stored the actual width?
7364 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7365 // which in reality did not work.. - it worked only for fixed sizes
7366 // for resizable we need to use actual sizes.
7367 var w = this.cm.getColumnWidth(this.cellIndex);
7368 if (!this.view.mainWrap) {
7370 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7375 // this was w-this.grid.minColumnWidth;
7376 // doesnt really make sense? - w = thie curren width or the rendered one?
7377 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7378 this.resetConstraints();
7379 this.setXConstraint(minw, 1000);
7380 this.setYConstraint(0, 0);
7381 this.minX = x - minw;
7382 this.maxX = x + 1000;
7384 if (!this.view.mainWrap) { // this is Bootstrap code..
7385 this.getDragEl().style.display='block';
7388 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7392 handleMouseDown : function(e){
7393 ev = Roo.EventObject.setEvent(e);
7394 var t = this.fly(ev.getTarget());
7395 if(t.hasClass("x-grid-split")){
7396 this.cellIndex = this.view.getCellIndex(t.dom);
7398 this.cm = this.grid.colModel;
7399 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7400 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7405 endDrag : function(e){
7406 this.view.headersDisabled = false;
7407 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7408 var diff = endX - this.startPos;
7410 var w = this.cm.getColumnWidth(this.cellIndex);
7411 if (!this.view.mainWrap) {
7414 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7417 autoOffset : function(){
7422 * Ext JS Library 1.1.1
7423 * Copyright(c) 2006-2007, Ext JS, LLC.
7425 * Originally Released Under LGPL - original licence link has changed is not relivant.
7428 * <script type="text/javascript">
7432 * @class Roo.grid.AbstractSelectionModel
7433 * @extends Roo.util.Observable
7435 * Abstract base class for grid SelectionModels. It provides the interface that should be
7436 * implemented by descendant classes. This class should not be directly instantiated.
7439 Roo.grid.AbstractSelectionModel = function(){
7440 this.locked = false;
7441 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7444 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7445 /** @ignore Called by the grid automatically. Do not call directly. */
7446 init : function(grid){
7452 * Locks the selections.
7459 * Unlocks the selections.
7461 unlock : function(){
7462 this.locked = false;
7466 * Returns true if the selections are locked.
7469 isLocked : function(){
7474 * Ext JS Library 1.1.1
7475 * Copyright(c) 2006-2007, Ext JS, LLC.
7477 * Originally Released Under LGPL - original licence link has changed is not relivant.
7480 * <script type="text/javascript">
7483 * @extends Roo.grid.AbstractSelectionModel
7484 * @class Roo.grid.RowSelectionModel
7485 * The default SelectionModel used by {@link Roo.grid.Grid}.
7486 * It supports multiple selections and keyboard selection/navigation.
7488 * @param {Object} config
7490 Roo.grid.RowSelectionModel = function(config){
7491 Roo.apply(this, config);
7492 this.selections = new Roo.util.MixedCollection(false, function(o){
7497 this.lastActive = false;
7501 * @event selectionchange
7502 * Fires when the selection changes
7503 * @param {SelectionModel} this
7505 "selectionchange" : true,
7507 * @event afterselectionchange
7508 * Fires after the selection changes (eg. by key press or clicking)
7509 * @param {SelectionModel} this
7511 "afterselectionchange" : true,
7513 * @event beforerowselect
7514 * Fires when a row is selected being selected, return false to cancel.
7515 * @param {SelectionModel} this
7516 * @param {Number} rowIndex The selected index
7517 * @param {Boolean} keepExisting False if other selections will be cleared
7519 "beforerowselect" : true,
7522 * Fires when a row is selected.
7523 * @param {SelectionModel} this
7524 * @param {Number} rowIndex The selected index
7525 * @param {Roo.data.Record} r The record
7529 * @event rowdeselect
7530 * Fires when a row is deselected.
7531 * @param {SelectionModel} this
7532 * @param {Number} rowIndex The selected index
7534 "rowdeselect" : true
7536 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7537 this.locked = false;
7540 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7542 * @cfg {Boolean} singleSelect
7543 * True to allow selection of only one row at a time (defaults to false)
7545 singleSelect : false,
7548 initEvents : function(){
7550 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7551 this.grid.on("mousedown", this.handleMouseDown, this);
7552 }else{ // allow click to work like normal
7553 this.grid.on("rowclick", this.handleDragableRowClick, this);
7555 // bootstrap does not have a view..
7556 var view = this.grid.view ? this.grid.view : this.grid;
7557 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7560 this.selectPrevious(e.shiftKey);
7561 }else if(this.last !== false && this.lastActive !== false){
7562 var last = this.last;
7563 this.selectRange(this.last, this.lastActive-1);
7564 view.focusRow(this.lastActive);
7569 this.selectFirstRow();
7571 this.fireEvent("afterselectionchange", this);
7573 "down" : function(e){
7575 this.selectNext(e.shiftKey);
7576 }else if(this.last !== false && this.lastActive !== false){
7577 var last = this.last;
7578 this.selectRange(this.last, this.lastActive+1);
7579 view.focusRow(this.lastActive);
7584 this.selectFirstRow();
7586 this.fireEvent("afterselectionchange", this);
7592 view.on("refresh", this.onRefresh, this);
7593 view.on("rowupdated", this.onRowUpdated, this);
7594 view.on("rowremoved", this.onRemove, this);
7598 onRefresh : function(){
7599 var ds = this.grid.ds, i, v = this.grid.view;
7600 var s = this.selections;
7602 if((i = ds.indexOfId(r.id)) != -1){
7604 s.add(ds.getAt(i)); // updating the selection relate data
7612 onRemove : function(v, index, r){
7613 this.selections.remove(r);
7617 onRowUpdated : function(v, index, r){
7618 if(this.isSelected(r)){
7619 v.onRowSelect(index);
7625 * @param {Array} records The records to select
7626 * @param {Boolean} keepExisting (optional) True to keep existing selections
7628 selectRecords : function(records, keepExisting){
7630 this.clearSelections();
7632 var ds = this.grid.ds;
7633 for(var i = 0, len = records.length; i < len; i++){
7634 this.selectRow(ds.indexOf(records[i]), true);
7639 * Gets the number of selected rows.
7642 getCount : function(){
7643 return this.selections.length;
7647 * Selects the first row in the grid.
7649 selectFirstRow : function(){
7654 * Select the last row.
7655 * @param {Boolean} keepExisting (optional) True to keep existing selections
7657 selectLastRow : function(keepExisting){
7658 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7662 * Selects the row immediately following the last selected row.
7663 * @param {Boolean} keepExisting (optional) True to keep existing selections
7665 selectNext : function(keepExisting){
7666 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7667 this.selectRow(this.last+1, keepExisting);
7668 var view = this.grid.view ? this.grid.view : this.grid;
7669 view.focusRow(this.last);
7674 * Selects the row that precedes the last selected row.
7675 * @param {Boolean} keepExisting (optional) True to keep existing selections
7677 selectPrevious : function(keepExisting){
7679 this.selectRow(this.last-1, keepExisting);
7680 var view = this.grid.view ? this.grid.view : this.grid;
7681 view.focusRow(this.last);
7686 * Returns the selected records
7687 * @return {Array} Array of selected records
7689 getSelections : function(){
7690 return [].concat(this.selections.items);
7694 * Returns the first selected record.
7697 getSelected : function(){
7698 return this.selections.itemAt(0);
7703 * Clears all selections.
7705 clearSelections : function(fast){
7710 var ds = this.grid.ds;
7711 var s = this.selections;
7713 this.deselectRow(ds.indexOfId(r.id));
7717 this.selections.clear();
7726 selectAll : function(){
7730 this.selections.clear();
7731 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7732 this.selectRow(i, true);
7737 * Returns True if there is a selection.
7740 hasSelection : function(){
7741 return this.selections.length > 0;
7745 * Returns True if the specified row is selected.
7746 * @param {Number/Record} record The record or index of the record to check
7749 isSelected : function(index){
7750 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7751 return (r && this.selections.key(r.id) ? true : false);
7755 * Returns True if the specified record id is selected.
7756 * @param {String} id The id of record to check
7759 isIdSelected : function(id){
7760 return (this.selections.key(id) ? true : false);
7764 handleMouseDown : function(e, t)
7766 var view = this.grid.view ? this.grid.view : this.grid;
7768 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7771 if(e.shiftKey && this.last !== false){
7772 var last = this.last;
7773 this.selectRange(last, rowIndex, e.ctrlKey);
7774 this.last = last; // reset the last
7775 view.focusRow(rowIndex);
7777 var isSelected = this.isSelected(rowIndex);
7778 if(e.button !== 0 && isSelected){
7779 view.focusRow(rowIndex);
7780 }else if(e.ctrlKey && isSelected){
7781 this.deselectRow(rowIndex);
7782 }else if(!isSelected){
7783 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7784 view.focusRow(rowIndex);
7787 this.fireEvent("afterselectionchange", this);
7790 handleDragableRowClick : function(grid, rowIndex, e)
7792 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7793 this.selectRow(rowIndex, false);
7794 var view = this.grid.view ? this.grid.view : this.grid;
7795 view.focusRow(rowIndex);
7796 this.fireEvent("afterselectionchange", this);
7801 * Selects multiple rows.
7802 * @param {Array} rows Array of the indexes of the row to select
7803 * @param {Boolean} keepExisting (optional) True to keep existing selections
7805 selectRows : function(rows, keepExisting){
7807 this.clearSelections();
7809 for(var i = 0, len = rows.length; i < len; i++){
7810 this.selectRow(rows[i], true);
7815 * Selects a range of rows. All rows in between startRow and endRow are also selected.
7816 * @param {Number} startRow The index of the first row in the range
7817 * @param {Number} endRow The index of the last row in the range
7818 * @param {Boolean} keepExisting (optional) True to retain existing selections
7820 selectRange : function(startRow, endRow, keepExisting){
7825 this.clearSelections();
7827 if(startRow <= endRow){
7828 for(var i = startRow; i <= endRow; i++){
7829 this.selectRow(i, true);
7832 for(var i = startRow; i >= endRow; i--){
7833 this.selectRow(i, true);
7839 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7840 * @param {Number} startRow The index of the first row in the range
7841 * @param {Number} endRow The index of the last row in the range
7843 deselectRange : function(startRow, endRow, preventViewNotify){
7847 for(var i = startRow; i <= endRow; i++){
7848 this.deselectRow(i, preventViewNotify);
7854 * @param {Number} row The index of the row to select
7855 * @param {Boolean} keepExisting (optional) True to keep existing selections
7857 selectRow : function(index, keepExisting, preventViewNotify){
7858 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7861 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7862 if(!keepExisting || this.singleSelect){
7863 this.clearSelections();
7865 var r = this.grid.ds.getAt(index);
7866 this.selections.add(r);
7867 this.last = this.lastActive = index;
7868 if(!preventViewNotify){
7869 var view = this.grid.view ? this.grid.view : this.grid;
7870 view.onRowSelect(index);
7872 this.fireEvent("rowselect", this, index, r);
7873 this.fireEvent("selectionchange", this);
7879 * @param {Number} row The index of the row to deselect
7881 deselectRow : function(index, preventViewNotify){
7885 if(this.last == index){
7888 if(this.lastActive == index){
7889 this.lastActive = false;
7891 var r = this.grid.ds.getAt(index);
7892 this.selections.remove(r);
7893 if(!preventViewNotify){
7894 var view = this.grid.view ? this.grid.view : this.grid;
7895 view.onRowDeselect(index);
7897 this.fireEvent("rowdeselect", this, index);
7898 this.fireEvent("selectionchange", this);
7902 restoreLast : function(){
7904 this.last = this._last;
7909 acceptsNav : function(row, col, cm){
7910 return !cm.isHidden(col) && cm.isCellEditable(col, row);
7914 onEditorKey : function(field, e){
7915 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7920 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7922 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7924 }else if(k == e.ENTER && !e.ctrlKey){
7928 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7930 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7932 }else if(k == e.ESC){
7936 g.startEditing(newCell[0], newCell[1]);
7941 * Ext JS Library 1.1.1
7942 * Copyright(c) 2006-2007, Ext JS, LLC.
7944 * Originally Released Under LGPL - original licence link has changed is not relivant.
7947 * <script type="text/javascript">
7952 * @class Roo.grid.ColumnModel
7953 * @extends Roo.util.Observable
7954 * This is the default implementation of a ColumnModel used by the Grid. It defines
7955 * the columns in the grid.
7958 var colModel = new Roo.grid.ColumnModel([
7959 {header: "Ticker", width: 60, sortable: true, locked: true},
7960 {header: "Company Name", width: 150, sortable: true},
7961 {header: "Market Cap.", width: 100, sortable: true},
7962 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7963 {header: "Employees", width: 100, sortable: true, resizable: false}
7968 * The config options listed for this class are options which may appear in each
7969 * individual column definition.
7970 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7972 * @param {Object} config An Array of column config objects. See this class's
7973 * config objects for details.
7975 Roo.grid.ColumnModel = function(config){
7977 * The config passed into the constructor
7979 this.config = []; //config;
7982 // if no id, create one
7983 // if the column does not have a dataIndex mapping,
7984 // map it to the order it is in the config
7985 for(var i = 0, len = config.length; i < len; i++){
7986 this.addColumn(config[i]);
7991 * The width of columns which have no width specified (defaults to 100)
7994 this.defaultWidth = 100;
7997 * Default sortable of columns which have no sortable specified (defaults to false)
8000 this.defaultSortable = false;
8004 * @event widthchange
8005 * Fires when the width of a column changes.
8006 * @param {ColumnModel} this
8007 * @param {Number} columnIndex The column index
8008 * @param {Number} newWidth The new width
8010 "widthchange": true,
8012 * @event headerchange
8013 * Fires when the text of a header changes.
8014 * @param {ColumnModel} this
8015 * @param {Number} columnIndex The column index
8016 * @param {Number} newText The new header text
8018 "headerchange": true,
8020 * @event hiddenchange
8021 * Fires when a column is hidden or "unhidden".
8022 * @param {ColumnModel} this
8023 * @param {Number} columnIndex The column index
8024 * @param {Boolean} hidden true if hidden, false otherwise
8026 "hiddenchange": true,
8028 * @event columnmoved
8029 * Fires when a column is moved.
8030 * @param {ColumnModel} this
8031 * @param {Number} oldIndex
8032 * @param {Number} newIndex
8034 "columnmoved" : true,
8036 * @event columlockchange
8037 * Fires when a column's locked state is changed
8038 * @param {ColumnModel} this
8039 * @param {Number} colIndex
8040 * @param {Boolean} locked true if locked
8042 "columnlockchange" : true
8044 Roo.grid.ColumnModel.superclass.constructor.call(this);
8046 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8048 * @cfg {String} header The header text to display in the Grid view.
8051 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8054 * @cfg {String} smHeader Header at Bootsrap Small width
8057 * @cfg {String} mdHeader Header at Bootsrap Medium width
8060 * @cfg {String} lgHeader Header at Bootsrap Large width
8063 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8066 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8067 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8068 * specified, the column's index is used as an index into the Record's data Array.
8071 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8072 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8075 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8076 * Defaults to the value of the {@link #defaultSortable} property.
8077 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8080 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
8083 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
8086 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8089 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8092 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8093 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8094 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8095 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8098 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
8101 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
8104 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
8107 * @cfg {String} cursor (Optional)
8110 * @cfg {String} tooltip (Optional)
8113 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8116 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8119 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8122 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8125 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8128 * Returns the id of the column at the specified index.
8129 * @param {Number} index The column index
8130 * @return {String} the id
8132 getColumnId : function(index){
8133 return this.config[index].id;
8137 * Returns the column for a specified id.
8138 * @param {String} id The column id
8139 * @return {Object} the column
8141 getColumnById : function(id){
8142 return this.lookup[id];
8147 * Returns the column Object for a specified dataIndex.
8148 * @param {String} dataIndex The column dataIndex
8149 * @return {Object|Boolean} the column or false if not found
8151 getColumnByDataIndex: function(dataIndex){
8152 var index = this.findColumnIndex(dataIndex);
8153 return index > -1 ? this.config[index] : false;
8157 * Returns the index for a specified column id.
8158 * @param {String} id The column id
8159 * @return {Number} the index, or -1 if not found
8161 getIndexById : function(id){
8162 for(var i = 0, len = this.config.length; i < len; i++){
8163 if(this.config[i].id == id){
8171 * Returns the index for a specified column dataIndex.
8172 * @param {String} dataIndex The column dataIndex
8173 * @return {Number} the index, or -1 if not found
8176 findColumnIndex : function(dataIndex){
8177 for(var i = 0, len = this.config.length; i < len; i++){
8178 if(this.config[i].dataIndex == dataIndex){
8186 moveColumn : function(oldIndex, newIndex){
8187 var c = this.config[oldIndex];
8188 this.config.splice(oldIndex, 1);
8189 this.config.splice(newIndex, 0, c);
8190 this.dataMap = null;
8191 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8194 isLocked : function(colIndex){
8195 return this.config[colIndex].locked === true;
8198 setLocked : function(colIndex, value, suppressEvent){
8199 if(this.isLocked(colIndex) == value){
8202 this.config[colIndex].locked = value;
8204 this.fireEvent("columnlockchange", this, colIndex, value);
8208 getTotalLockedWidth : function(){
8210 for(var i = 0; i < this.config.length; i++){
8211 if(this.isLocked(i) && !this.isHidden(i)){
8212 this.totalWidth += this.getColumnWidth(i);
8218 getLockedCount : function(){
8219 for(var i = 0, len = this.config.length; i < len; i++){
8220 if(!this.isLocked(i)){
8225 return this.config.length;
8229 * Returns the number of columns.
8232 getColumnCount : function(visibleOnly){
8233 if(visibleOnly === true){
8235 for(var i = 0, len = this.config.length; i < len; i++){
8236 if(!this.isHidden(i)){
8242 return this.config.length;
8246 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8247 * @param {Function} fn
8248 * @param {Object} scope (optional)
8249 * @return {Array} result
8251 getColumnsBy : function(fn, scope){
8253 for(var i = 0, len = this.config.length; i < len; i++){
8254 var c = this.config[i];
8255 if(fn.call(scope||this, c, i) === true){
8263 * Returns true if the specified column is sortable.
8264 * @param {Number} col The column index
8267 isSortable : function(col){
8268 if(typeof this.config[col].sortable == "undefined"){
8269 return this.defaultSortable;
8271 return this.config[col].sortable;
8275 * Returns the rendering (formatting) function defined for the column.
8276 * @param {Number} col The column index.
8277 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8279 getRenderer : function(col){
8280 if(!this.config[col].renderer){
8281 return Roo.grid.ColumnModel.defaultRenderer;
8283 return this.config[col].renderer;
8287 * Sets the rendering (formatting) function for a column.
8288 * @param {Number} col The column index
8289 * @param {Function} fn The function to use to process the cell's raw data
8290 * to return HTML markup for the grid view. The render function is called with
8291 * the following parameters:<ul>
8292 * <li>Data value.</li>
8293 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8294 * <li>css A CSS style string to apply to the table cell.</li>
8295 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8296 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8297 * <li>Row index</li>
8298 * <li>Column index</li>
8299 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8301 setRenderer : function(col, fn){
8302 this.config[col].renderer = fn;
8306 * Returns the width for the specified column.
8307 * @param {Number} col The column index
8308 * @param (optional) {String} gridSize bootstrap width size.
8311 getColumnWidth : function(col, gridSize)
8313 var cfg = this.config[col];
8315 if (typeof(gridSize) == 'undefined') {
8316 return cfg.width * 1 || this.defaultWidth;
8318 if (gridSize === false) { // if we set it..
8319 return cfg.width || false;
8321 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8323 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8324 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8327 return cfg[ sizes[i] ];
8334 * Sets the width for a column.
8335 * @param {Number} col The column index
8336 * @param {Number} width The new width
8338 setColumnWidth : function(col, width, suppressEvent){
8339 this.config[col].width = width;
8340 this.totalWidth = null;
8342 this.fireEvent("widthchange", this, col, width);
8347 * Returns the total width of all columns.
8348 * @param {Boolean} includeHidden True to include hidden column widths
8351 getTotalWidth : function(includeHidden){
8352 if(!this.totalWidth){
8353 this.totalWidth = 0;
8354 for(var i = 0, len = this.config.length; i < len; i++){
8355 if(includeHidden || !this.isHidden(i)){
8356 this.totalWidth += this.getColumnWidth(i);
8360 return this.totalWidth;
8364 * Returns the header for the specified column.
8365 * @param {Number} col The column index
8368 getColumnHeader : function(col){
8369 return this.config[col].header;
8373 * Sets the header for a column.
8374 * @param {Number} col The column index
8375 * @param {String} header The new header
8377 setColumnHeader : function(col, header){
8378 this.config[col].header = header;
8379 this.fireEvent("headerchange", this, col, header);
8383 * Returns the tooltip for the specified column.
8384 * @param {Number} col The column index
8387 getColumnTooltip : function(col){
8388 return this.config[col].tooltip;
8391 * Sets the tooltip for a column.
8392 * @param {Number} col The column index
8393 * @param {String} tooltip The new tooltip
8395 setColumnTooltip : function(col, tooltip){
8396 this.config[col].tooltip = tooltip;
8400 * Returns the dataIndex for the specified column.
8401 * @param {Number} col The column index
8404 getDataIndex : function(col){
8405 return this.config[col].dataIndex;
8409 * Sets the dataIndex for a column.
8410 * @param {Number} col The column index
8411 * @param {Number} dataIndex The new dataIndex
8413 setDataIndex : function(col, dataIndex){
8414 this.config[col].dataIndex = dataIndex;
8420 * Returns true if the cell is editable.
8421 * @param {Number} colIndex The column index
8422 * @param {Number} rowIndex The row index - this is nto actually used..?
8425 isCellEditable : function(colIndex, rowIndex){
8426 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8430 * Returns the editor defined for the cell/column.
8431 * return false or null to disable editing.
8432 * @param {Number} colIndex The column index
8433 * @param {Number} rowIndex The row index
8436 getCellEditor : function(colIndex, rowIndex){
8437 return this.config[colIndex].editor;
8441 * Sets if a column is editable.
8442 * @param {Number} col The column index
8443 * @param {Boolean} editable True if the column is editable
8445 setEditable : function(col, editable){
8446 this.config[col].editable = editable;
8451 * Returns true if the column is hidden.
8452 * @param {Number} colIndex The column index
8455 isHidden : function(colIndex){
8456 return this.config[colIndex].hidden;
8461 * Returns true if the column width cannot be changed
8463 isFixed : function(colIndex){
8464 return this.config[colIndex].fixed;
8468 * Returns true if the column can be resized
8471 isResizable : function(colIndex){
8472 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8475 * Sets if a column is hidden.
8476 * @param {Number} colIndex The column index
8477 * @param {Boolean} hidden True if the column is hidden
8479 setHidden : function(colIndex, hidden){
8480 this.config[colIndex].hidden = hidden;
8481 this.totalWidth = null;
8482 this.fireEvent("hiddenchange", this, colIndex, hidden);
8486 * Sets the editor for a column.
8487 * @param {Number} col The column index
8488 * @param {Object} editor The editor object
8490 setEditor : function(col, editor){
8491 this.config[col].editor = editor;
8494 * Add a column (experimental...) - defaults to adding to the end..
8495 * @param {Object} config
8497 addColumn : function(c)
8500 var i = this.config.length;
8503 if(typeof c.dataIndex == "undefined"){
8506 if(typeof c.renderer == "string"){
8507 c.renderer = Roo.util.Format[c.renderer];
8509 if(typeof c.id == "undefined"){
8512 if(c.editor && c.editor.xtype){
8513 c.editor = Roo.factory(c.editor, Roo.grid);
8515 if(c.editor && c.editor.isFormField){
8516 c.editor = new Roo.grid.GridEditor(c.editor);
8518 this.lookup[c.id] = c;
8523 Roo.grid.ColumnModel.defaultRenderer = function(value)
8525 if(typeof value == "object") {
8528 if(typeof value == "string" && value.length < 1){
8532 return String.format("{0}", value);
8535 // Alias for backwards compatibility
8536 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8539 * Ext JS Library 1.1.1
8540 * Copyright(c) 2006-2007, Ext JS, LLC.
8542 * Originally Released Under LGPL - original licence link has changed is not relivant.
8545 * <script type="text/javascript">
8549 * @class Roo.LoadMask
8550 * A simple utility class for generically masking elements while loading data. If the element being masked has
8551 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8552 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8553 * element's UpdateManager load indicator and will be destroyed after the initial load.
8555 * Create a new LoadMask
8556 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8557 * @param {Object} config The config object
8559 Roo.LoadMask = function(el, config){
8560 this.el = Roo.get(el);
8561 Roo.apply(this, config);
8563 this.store.on('beforeload', this.onBeforeLoad, this);
8564 this.store.on('load', this.onLoad, this);
8565 this.store.on('loadexception', this.onLoadException, this);
8566 this.removeMask = false;
8568 var um = this.el.getUpdateManager();
8569 um.showLoadIndicator = false; // disable the default indicator
8570 um.on('beforeupdate', this.onBeforeLoad, this);
8571 um.on('update', this.onLoad, this);
8572 um.on('failure', this.onLoad, this);
8573 this.removeMask = true;
8577 Roo.LoadMask.prototype = {
8579 * @cfg {Boolean} removeMask
8580 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8581 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
8586 * The text to display in a centered loading message box (defaults to 'Loading...')
8590 * @cfg {String} msgCls
8591 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8593 msgCls : 'x-mask-loading',
8596 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8602 * Disables the mask to prevent it from being displayed
8604 disable : function(){
8605 this.disabled = true;
8609 * Enables the mask so that it can be displayed
8611 enable : function(){
8612 this.disabled = false;
8615 onLoadException : function()
8619 if (typeof(arguments[3]) != 'undefined') {
8620 Roo.MessageBox.alert("Error loading",arguments[3]);
8624 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8625 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8632 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8637 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8641 onBeforeLoad : function(){
8643 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8648 destroy : function(){
8650 this.store.un('beforeload', this.onBeforeLoad, this);
8651 this.store.un('load', this.onLoad, this);
8652 this.store.un('loadexception', this.onLoadException, this);
8654 var um = this.el.getUpdateManager();
8655 um.un('beforeupdate', this.onBeforeLoad, this);
8656 um.un('update', this.onLoad, this);
8657 um.un('failure', this.onLoad, this);
8661 * @class Roo.bootstrap.Table
8663 * @extends Roo.bootstrap.Component
8664 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
8665 * Similar to Roo.grid.Grid
8667 var table = Roo.factory({
8669 xns : Roo.bootstrap,
8670 autoSizeColumns: true,
8677 sortInfo : { direction : 'ASC', field: 'name' },
8679 xtype : 'HttpProxy',
8682 url : 'https://example.com/some.data.url.json'
8685 xtype : 'JsonReader',
8687 fields : [ 'id', 'name', whatever' ],
8694 xtype : 'ColumnModel',
8698 dataIndex : 'is_in_group',
8701 renderer : function(v, x , r) {
8703 return String.format("{0}", v)
8709 xtype : 'RowSelectionModel',
8710 xns : Roo.bootstrap.Table
8711 // you can add listeners to catch selection change here....
8717 grid.render(Roo.get("some-div"));
8720 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
8725 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
8726 * @cfg {Roo.data.Store} store The data store to use
8727 * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8729 * @cfg {String} cls table class
8732 * @cfg {boolean} striped Should the rows be alternative striped
8733 * @cfg {boolean} bordered Add borders to the table
8734 * @cfg {boolean} hover Add hover highlighting
8735 * @cfg {boolean} condensed Format condensed
8736 * @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,
8737 * also adds table-responsive (see bootstrap docs for details)
8738 * @cfg {Boolean} loadMask (true|false) default false
8739 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8740 * @cfg {Boolean} headerShow (true|false) generate thead, default true
8741 * @cfg {Boolean} rowSelection (true|false) default false
8742 * @cfg {Boolean} cellSelection (true|false) default false
8743 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8744 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
8745 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8746 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8747 * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8748 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
8751 * Create a new Table
8752 * @param {Object} config The config object
8755 Roo.bootstrap.Table = function(config)
8757 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8760 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8761 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8762 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8763 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8765 this.view = this; // compat with grid.
8767 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8769 this.sm.grid = this;
8770 this.selModel = Roo.factory(this.sm, Roo.grid);
8771 this.sm = this.selModel;
8772 this.sm.xmodule = this.xmodule || false;
8775 if (this.cm && typeof(this.cm.config) == 'undefined') {
8776 this.colModel = new Roo.grid.ColumnModel(this.cm);
8777 this.cm = this.colModel;
8778 this.cm.xmodule = this.xmodule || false;
8781 this.store= Roo.factory(this.store, Roo.data);
8782 this.ds = this.store;
8783 this.ds.xmodule = this.xmodule || false;
8786 if (this.footer && this.store) {
8787 this.footer.dataSource = this.ds;
8788 this.footer = Roo.factory(this.footer);
8795 * Fires when a cell is clicked
8796 * @param {Roo.bootstrap.Table} this
8797 * @param {Roo.Element} el
8798 * @param {Number} rowIndex
8799 * @param {Number} columnIndex
8800 * @param {Roo.EventObject} e
8804 * @event celldblclick
8805 * Fires when a cell is double clicked
8806 * @param {Roo.bootstrap.Table} this
8807 * @param {Roo.Element} el
8808 * @param {Number} rowIndex
8809 * @param {Number} columnIndex
8810 * @param {Roo.EventObject} e
8812 "celldblclick" : true,
8815 * Fires when a row is clicked
8816 * @param {Roo.bootstrap.Table} this
8817 * @param {Roo.Element} el
8818 * @param {Number} rowIndex
8819 * @param {Roo.EventObject} e
8823 * @event rowdblclick
8824 * Fires when a row is double clicked
8825 * @param {Roo.bootstrap.Table} this
8826 * @param {Roo.Element} el
8827 * @param {Number} rowIndex
8828 * @param {Roo.EventObject} e
8830 "rowdblclick" : true,
8833 * Fires when a mouseover occur
8834 * @param {Roo.bootstrap.Table} this
8835 * @param {Roo.Element} el
8836 * @param {Number} rowIndex
8837 * @param {Number} columnIndex
8838 * @param {Roo.EventObject} e
8843 * Fires when a mouseout occur
8844 * @param {Roo.bootstrap.Table} this
8845 * @param {Roo.Element} el
8846 * @param {Number} rowIndex
8847 * @param {Number} columnIndex
8848 * @param {Roo.EventObject} e
8853 * Fires when a row is rendered, so you can change add a style to it.
8854 * @param {Roo.bootstrap.Table} this
8855 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8859 * @event rowsrendered
8860 * Fires when all the rows have been rendered
8861 * @param {Roo.bootstrap.Table} this
8863 'rowsrendered' : true,
8865 * @event contextmenu
8866 * The raw contextmenu event for the entire grid.
8867 * @param {Roo.EventObject} e
8869 "contextmenu" : true,
8871 * @event rowcontextmenu
8872 * Fires when a row is right clicked
8873 * @param {Roo.bootstrap.Table} this
8874 * @param {Number} rowIndex
8875 * @param {Roo.EventObject} e
8877 "rowcontextmenu" : true,
8879 * @event cellcontextmenu
8880 * Fires when a cell is right clicked
8881 * @param {Roo.bootstrap.Table} this
8882 * @param {Number} rowIndex
8883 * @param {Number} cellIndex
8884 * @param {Roo.EventObject} e
8886 "cellcontextmenu" : true,
8888 * @event headercontextmenu
8889 * Fires when a header is right clicked
8890 * @param {Roo.bootstrap.Table} this
8891 * @param {Number} columnIndex
8892 * @param {Roo.EventObject} e
8894 "headercontextmenu" : true,
8897 * The raw mousedown event for the entire grid.
8898 * @param {Roo.EventObject} e
8905 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8921 enableColumnResize: true,
8923 rowSelection : false,
8924 cellSelection : false,
8927 minColumnWidth : 50,
8929 // Roo.Element - the tbody
8930 bodyEl: false, // <tbody> Roo.Element - thead element
8931 headEl: false, // <thead> Roo.Element - thead element
8932 resizeProxy : false, // proxy element for dragging?
8936 container: false, // used by gridpanel...
8942 auto_hide_footer : false,
8944 view: false, // actually points to this..
8946 getAutoCreate : function()
8948 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8955 // this get's auto added by panel.Grid
8956 if (this.scrollBody) {
8957 cfg.cls += ' table-body-fixed';
8960 cfg.cls += ' table-striped';
8964 cfg.cls += ' table-hover';
8966 if (this.bordered) {
8967 cfg.cls += ' table-bordered';
8969 if (this.condensed) {
8970 cfg.cls += ' table-condensed';
8973 if (this.responsive) {
8974 cfg.cls += ' table-responsive';
8978 cfg.cls+= ' ' +this.cls;
8984 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8987 if(this.store || this.cm){
8988 if(this.headerShow){
8989 cfg.cn.push(this.renderHeader());
8992 cfg.cn.push(this.renderBody());
8994 if(this.footerShow){
8995 cfg.cn.push(this.renderFooter());
8997 // where does this come from?
8998 //cfg.cls+= ' TableGrid';
9001 return { cn : [ cfg ] };
9004 initEvents : function()
9006 if(!this.store || !this.cm){
9009 if (this.selModel) {
9010 this.selModel.initEvents();
9014 //Roo.log('initEvents with ds!!!!');
9016 this.bodyEl = this.el.select('tbody', true).first();
9017 this.headEl = this.el.select('thead', true).first();
9018 this.mainFoot = this.el.select('tfoot', true).first();
9023 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9024 e.on('click', this.sort, this);
9028 // why is this done????? = it breaks dialogs??
9029 //this.parent().el.setStyle('position', 'relative');
9033 this.footer.parentId = this.id;
9034 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9037 this.el.select('tfoot tr td').first().addClass('hide');
9042 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9045 this.store.on('load', this.onLoad, this);
9046 this.store.on('beforeload', this.onBeforeLoad, this);
9047 this.store.on('update', this.onUpdate, this);
9048 this.store.on('add', this.onAdd, this);
9049 this.store.on("clear", this.clear, this);
9051 this.el.on("contextmenu", this.onContextMenu, this);
9054 this.cm.on("headerchange", this.onHeaderChange, this);
9055 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9057 //?? does bodyEl get replaced on render?
9058 this.bodyEl.on("click", this.onClick, this);
9059 this.bodyEl.on("dblclick", this.onDblClick, this);
9060 this.bodyEl.on('scroll', this.onBodyScroll, this);
9062 // guessing mainbody will work - this relays usually caught by selmodel at present.
9063 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9066 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9069 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9070 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9075 // Compatibility with grid - we implement all the view features at present.
9076 getView : function()
9081 initCSS : function()
9085 var cm = this.cm, styles = [];
9086 this.CSS.removeStyleSheet(this.id + '-cssrules');
9087 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9088 // we can honour xs/sm/md/xl as widths...
9089 // we first have to decide what widht we are currently at...
9090 var sz = Roo.getGridSize();
9094 var cols = []; // visable cols.
9096 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9097 var w = cm.getColumnWidth(i, false);
9099 cols.push( { rel : false, abs : 0 });
9103 cols.push( { rel : false, abs : w });
9105 last = i; // not really..
9108 var w = cm.getColumnWidth(i, sz);
9113 cols.push( { rel : w, abs : false });
9116 var avail = this.bodyEl.dom.clientWidth - total_abs;
9118 var unitWidth = Math.floor(avail / total);
9119 var rem = avail - (unitWidth * total);
9121 var hidden, width, pos = 0 , splithide , left;
9122 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9124 hidden = 'display:none;';
9126 width = 'width:0px;';
9128 if(!cm.isHidden(i)){
9132 // we can honour xs/sm/md/xl ?
9133 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9135 hidden = 'display:none;';
9137 // width should return a small number...
9139 w+=rem; // add the remaining with..
9142 left = "left:" + (pos -4) + "px;";
9143 width = "width:" + w+ "px;";
9146 if (this.responsive) {
9149 hidden = cm.isHidden(i) ? 'display:none;' : '';
9150 splithide = 'display: none;';
9153 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9156 splithide = 'display:none;';
9159 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9160 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9165 //Roo.log(styles.join(''));
9166 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9172 onContextMenu : function(e, t)
9174 this.processEvent("contextmenu", e);
9177 processEvent : function(name, e)
9179 if (name != 'touchstart' ) {
9180 this.fireEvent(name, e);
9183 var t = e.getTarget();
9185 var cell = Roo.get(t);
9191 if(cell.findParent('tfoot', false, true)){
9195 if(cell.findParent('thead', false, true)){
9197 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9198 cell = Roo.get(t).findParent('th', false, true);
9200 Roo.log("failed to find th in thead?");
9201 Roo.log(e.getTarget());
9206 var cellIndex = cell.dom.cellIndex;
9208 var ename = name == 'touchstart' ? 'click' : name;
9209 this.fireEvent("header" + ename, this, cellIndex, e);
9214 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9215 cell = Roo.get(t).findParent('td', false, true);
9217 Roo.log("failed to find th in tbody?");
9218 Roo.log(e.getTarget());
9223 var row = cell.findParent('tr', false, true);
9224 var cellIndex = cell.dom.cellIndex;
9225 var rowIndex = row.dom.rowIndex - 1;
9229 this.fireEvent("row" + name, this, rowIndex, e);
9233 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9239 onMouseover : function(e, el)
9241 var cell = Roo.get(el);
9247 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9248 cell = cell.findParent('td', false, true);
9251 var row = cell.findParent('tr', false, true);
9252 var cellIndex = cell.dom.cellIndex;
9253 var rowIndex = row.dom.rowIndex - 1; // start from 0
9255 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9259 onMouseout : function(e, el)
9261 var cell = Roo.get(el);
9267 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9268 cell = cell.findParent('td', false, true);
9271 var row = cell.findParent('tr', false, true);
9272 var cellIndex = cell.dom.cellIndex;
9273 var rowIndex = row.dom.rowIndex - 1; // start from 0
9275 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9279 onClick : function(e, el)
9281 var cell = Roo.get(el);
9283 if(!cell || (!this.cellSelection && !this.rowSelection)){
9287 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9288 cell = cell.findParent('td', false, true);
9291 if(!cell || typeof(cell) == 'undefined'){
9295 var row = cell.findParent('tr', false, true);
9297 if(!row || typeof(row) == 'undefined'){
9301 var cellIndex = cell.dom.cellIndex;
9302 var rowIndex = this.getRowIndex(row);
9304 // why??? - should these not be based on SelectionModel?
9305 //if(this.cellSelection){
9306 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9309 //if(this.rowSelection){
9310 this.fireEvent('rowclick', this, row, rowIndex, e);
9315 onDblClick : function(e,el)
9317 var cell = Roo.get(el);
9319 if(!cell || (!this.cellSelection && !this.rowSelection)){
9323 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9324 cell = cell.findParent('td', false, true);
9327 if(!cell || typeof(cell) == 'undefined'){
9331 var row = cell.findParent('tr', false, true);
9333 if(!row || typeof(row) == 'undefined'){
9337 var cellIndex = cell.dom.cellIndex;
9338 var rowIndex = this.getRowIndex(row);
9340 if(this.cellSelection){
9341 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9344 if(this.rowSelection){
9345 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9348 findRowIndex : function(el)
9350 var cell = Roo.get(el);
9354 var row = cell.findParent('tr', false, true);
9356 if(!row || typeof(row) == 'undefined'){
9359 return this.getRowIndex(row);
9361 sort : function(e,el)
9363 var col = Roo.get(el);
9365 if(!col.hasClass('sortable')){
9369 var sort = col.attr('sort');
9372 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9376 this.store.sortInfo = {field : sort, direction : dir};
9379 Roo.log("calling footer first");
9380 this.footer.onClick('first');
9383 this.store.load({ params : { start : 0 } });
9387 renderHeader : function()
9395 this.totalWidth = 0;
9397 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9399 var config = cm.config[i];
9403 cls : 'x-hcol-' + i,
9406 html: cm.getColumnHeader(i)
9409 var tooltip = cm.getColumnTooltip(i);
9411 c.tooltip = tooltip;
9417 if(typeof(config.sortable) != 'undefined' && config.sortable){
9418 c.cls += ' sortable';
9419 c.html = '<i class="fa"></i>' + c.html;
9422 // could use BS4 hidden-..-down
9424 if(typeof(config.lgHeader) != 'undefined'){
9425 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9428 if(typeof(config.mdHeader) != 'undefined'){
9429 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9432 if(typeof(config.smHeader) != 'undefined'){
9433 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9436 if(typeof(config.xsHeader) != 'undefined'){
9437 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9444 if(typeof(config.tooltip) != 'undefined'){
9445 c.tooltip = config.tooltip;
9448 if(typeof(config.colspan) != 'undefined'){
9449 c.colspan = config.colspan;
9452 // hidden is handled by CSS now
9454 if(typeof(config.dataIndex) != 'undefined'){
9455 c.sort = config.dataIndex;
9460 if(typeof(config.align) != 'undefined' && config.align.length){
9461 c.style += ' text-align:' + config.align + ';';
9464 /* width is done in CSS
9465 *if(typeof(config.width) != 'undefined'){
9466 c.style += ' width:' + config.width + 'px;';
9467 this.totalWidth += config.width;
9469 this.totalWidth += 100; // assume minimum of 100 per column?
9473 if(typeof(config.cls) != 'undefined'){
9474 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9476 // this is the bit that doesnt reall work at all...
9478 if (this.responsive) {
9481 ['xs','sm','md','lg'].map(function(size){
9483 if(typeof(config[size]) == 'undefined'){
9487 if (!config[size]) { // 0 = hidden
9488 // BS 4 '0' is treated as hide that column and below.
9489 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9493 c.cls += ' col-' + size + '-' + config[size] + (
9494 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9502 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9513 renderBody : function()
9523 colspan : this.cm.getColumnCount()
9533 renderFooter : function()
9543 colspan : this.cm.getColumnCount()
9557 // Roo.log('ds onload');
9562 var ds = this.store;
9564 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9565 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9566 if (_this.store.sortInfo) {
9568 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9569 e.select('i', true).addClass(['fa-arrow-up']);
9572 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9573 e.select('i', true).addClass(['fa-arrow-down']);
9578 var tbody = this.bodyEl;
9580 if(ds.getCount() > 0){
9581 ds.data.each(function(d,rowIndex){
9582 var row = this.renderRow(cm, ds, rowIndex);
9584 tbody.createChild(row);
9588 if(row.cellObjects.length){
9589 Roo.each(row.cellObjects, function(r){
9590 _this.renderCellObject(r);
9597 var tfoot = this.el.select('tfoot', true).first();
9599 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9601 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9603 var total = this.ds.getTotalCount();
9605 if(this.footer.pageSize < total){
9606 this.mainFoot.show();
9610 Roo.each(this.el.select('tbody td', true).elements, function(e){
9611 e.on('mouseover', _this.onMouseover, _this);
9614 Roo.each(this.el.select('tbody td', true).elements, function(e){
9615 e.on('mouseout', _this.onMouseout, _this);
9617 this.fireEvent('rowsrendered', this);
9621 this.initCSS(); /// resize cols
9627 onUpdate : function(ds,record)
9629 this.refreshRow(record);
9633 onRemove : function(ds, record, index, isUpdate){
9634 if(isUpdate !== true){
9635 this.fireEvent("beforerowremoved", this, index, record);
9637 var bt = this.bodyEl.dom;
9639 var rows = this.el.select('tbody > tr', true).elements;
9641 if(typeof(rows[index]) != 'undefined'){
9642 bt.removeChild(rows[index].dom);
9645 // if(bt.rows[index]){
9646 // bt.removeChild(bt.rows[index]);
9649 if(isUpdate !== true){
9650 //this.stripeRows(index);
9651 //this.syncRowHeights(index, index);
9653 this.fireEvent("rowremoved", this, index, record);
9657 onAdd : function(ds, records, rowIndex)
9659 //Roo.log('on Add called');
9660 // - note this does not handle multiple adding very well..
9661 var bt = this.bodyEl.dom;
9662 for (var i =0 ; i < records.length;i++) {
9663 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9664 //Roo.log(records[i]);
9665 //Roo.log(this.store.getAt(rowIndex+i));
9666 this.insertRow(this.store, rowIndex + i, false);
9673 refreshRow : function(record){
9674 var ds = this.store, index;
9675 if(typeof record == 'number'){
9677 record = ds.getAt(index);
9679 index = ds.indexOf(record);
9681 return; // should not happen - but seems to
9684 this.insertRow(ds, index, true);
9686 this.onRemove(ds, record, index+1, true);
9688 //this.syncRowHeights(index, index);
9690 this.fireEvent("rowupdated", this, index, record);
9692 // private - called by RowSelection
9693 onRowSelect : function(rowIndex){
9694 var row = this.getRowDom(rowIndex);
9695 row.addClass(['bg-info','info']);
9697 // private - called by RowSelection
9698 onRowDeselect : function(rowIndex)
9703 var row = this.getRowDom(rowIndex);
9704 row.removeClass(['bg-info','info']);
9707 * Focuses the specified row.
9708 * @param {Number} row The row index
9710 focusRow : function(row)
9712 //Roo.log('GridView.focusRow');
9713 var x = this.bodyEl.dom.scrollLeft;
9714 this.focusCell(row, 0, false);
9715 this.bodyEl.dom.scrollLeft = x;
9719 * Focuses the specified cell.
9720 * @param {Number} row The row index
9721 * @param {Number} col The column index
9722 * @param {Boolean} hscroll false to disable horizontal scrolling
9724 focusCell : function(row, col, hscroll)
9726 //Roo.log('GridView.focusCell');
9727 var el = this.ensureVisible(row, col, hscroll);
9728 // not sure what focusEL achives = it's a <a> pos relative
9729 //this.focusEl.alignTo(el, "tl-tl");
9731 // this.focusEl.focus();
9733 // this.focusEl.focus.defer(1, this.focusEl);
9738 * Scrolls the specified cell into view
9739 * @param {Number} row The row index
9740 * @param {Number} col The column index
9741 * @param {Boolean} hscroll false to disable horizontal scrolling
9743 ensureVisible : function(row, col, hscroll)
9745 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9746 //return null; //disable for testing.
9747 if(typeof row != "number"){
9750 if(row < 0 && row >= this.ds.getCount()){
9753 col = (col !== undefined ? col : 0);
9755 while(cm.isHidden(col)){
9759 var el = this.getCellDom(row, col);
9763 var c = this.bodyEl.dom;
9765 var ctop = parseInt(el.offsetTop, 10);
9766 var cleft = parseInt(el.offsetLeft, 10);
9767 var cbot = ctop + el.offsetHeight;
9768 var cright = cleft + el.offsetWidth;
9770 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9771 var ch = 0; //?? header is not withing the area?
9772 var stop = parseInt(c.scrollTop, 10);
9773 var sleft = parseInt(c.scrollLeft, 10);
9774 var sbot = stop + ch;
9775 var sright = sleft + c.clientWidth;
9777 Roo.log('GridView.ensureVisible:' +
9779 ' c.clientHeight:' + c.clientHeight +
9780 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9789 //Roo.log("set scrolltop to ctop DISABLE?");
9790 }else if(cbot > sbot){
9791 //Roo.log("set scrolltop to cbot-ch");
9792 c.scrollTop = cbot-ch;
9795 if(hscroll !== false){
9797 c.scrollLeft = cleft;
9798 }else if(cright > sright){
9799 c.scrollLeft = cright-c.clientWidth;
9807 insertRow : function(dm, rowIndex, isUpdate){
9810 this.fireEvent("beforerowsinserted", this, rowIndex);
9812 //var s = this.getScrollState();
9813 var row = this.renderRow(this.cm, this.store, rowIndex);
9814 // insert before rowIndex..
9815 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9819 if(row.cellObjects.length){
9820 Roo.each(row.cellObjects, function(r){
9821 _this.renderCellObject(r);
9826 this.fireEvent("rowsinserted", this, rowIndex);
9827 //this.syncRowHeights(firstRow, lastRow);
9828 //this.stripeRows(firstRow);
9835 getRowDom : function(rowIndex)
9837 var rows = this.el.select('tbody > tr', true).elements;
9839 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9842 getCellDom : function(rowIndex, colIndex)
9844 var row = this.getRowDom(rowIndex);
9845 if (row === false) {
9848 var cols = row.select('td', true).elements;
9849 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9853 // returns the object tree for a tr..
9856 renderRow : function(cm, ds, rowIndex)
9858 var d = ds.getAt(rowIndex);
9862 cls : 'x-row-' + rowIndex,
9866 var cellObjects = [];
9868 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9869 var config = cm.config[i];
9871 var renderer = cm.getRenderer(i);
9875 if(typeof(renderer) !== 'undefined'){
9876 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9878 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9879 // and are rendered into the cells after the row is rendered - using the id for the element.
9881 if(typeof(value) === 'object'){
9891 rowIndex : rowIndex,
9896 this.fireEvent('rowclass', this, rowcfg);
9900 // this might end up displaying HTML?
9901 // this is too messy... - better to only do it on columsn you know are going to be too long
9902 //tooltip : (typeof(value) === 'object') ? '' : value,
9903 cls : rowcfg.rowClass + ' x-col-' + i,
9905 html: (typeof(value) === 'object') ? '' : value
9912 if(typeof(config.colspan) != 'undefined'){
9913 td.colspan = config.colspan;
9918 if(typeof(config.align) != 'undefined' && config.align.length){
9919 td.style += ' text-align:' + config.align + ';';
9921 if(typeof(config.valign) != 'undefined' && config.valign.length){
9922 td.style += ' vertical-align:' + config.valign + ';';
9925 if(typeof(config.width) != 'undefined'){
9926 td.style += ' width:' + config.width + 'px;';
9930 if(typeof(config.cursor) != 'undefined'){
9931 td.style += ' cursor:' + config.cursor + ';';
9934 if(typeof(config.cls) != 'undefined'){
9935 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9937 if (this.responsive) {
9938 ['xs','sm','md','lg'].map(function(size){
9940 if(typeof(config[size]) == 'undefined'){
9946 if (!config[size]) { // 0 = hidden
9947 // BS 4 '0' is treated as hide that column and below.
9948 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9952 td.cls += ' col-' + size + '-' + config[size] + (
9953 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9963 row.cellObjects = cellObjects;
9971 onBeforeLoad : function()
9980 this.el.select('tbody', true).first().dom.innerHTML = '';
9983 * Show or hide a row.
9984 * @param {Number} rowIndex to show or hide
9985 * @param {Boolean} state hide
9987 setRowVisibility : function(rowIndex, state)
9989 var bt = this.bodyEl.dom;
9991 var rows = this.el.select('tbody > tr', true).elements;
9993 if(typeof(rows[rowIndex]) == 'undefined'){
9996 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10001 getSelectionModel : function(){
10002 if(!this.selModel){
10003 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10005 return this.selModel;
10008 * Render the Roo.bootstrap object from renderder
10010 renderCellObject : function(r)
10014 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10016 var t = r.cfg.render(r.container);
10019 Roo.each(r.cfg.cn, function(c){
10021 container: t.getChildContainer(),
10024 _this.renderCellObject(child);
10029 * get the Row Index from a dom element.
10030 * @param {Roo.Element} row The row to look for
10031 * @returns {Number} the row
10033 getRowIndex : function(row)
10037 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10048 * get the header TH element for columnIndex
10049 * @param {Number} columnIndex
10050 * @returns {Roo.Element}
10052 getHeaderIndex: function(colIndex)
10054 var cols = this.headEl.select('th', true).elements;
10055 return cols[colIndex];
10058 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10059 * @param {domElement} cell to look for
10060 * @returns {Number} the column
10062 getCellIndex : function(cell)
10064 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10066 return parseInt(id[1], 10);
10071 * Returns the grid's underlying element = used by panel.Grid
10072 * @return {Element} The element
10074 getGridEl : function(){
10078 * Forces a resize - used by panel.Grid
10079 * @return {Element} The element
10081 autoSize : function()
10083 //var ctr = Roo.get(this.container.dom.parentElement);
10084 var ctr = Roo.get(this.el.dom);
10086 var thd = this.getGridEl().select('thead',true).first();
10087 var tbd = this.getGridEl().select('tbody', true).first();
10088 var tfd = this.getGridEl().select('tfoot', true).first();
10090 var cw = ctr.getWidth();
10091 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10095 tbd.setWidth(ctr.getWidth());
10096 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10097 // this needs fixing for various usage - currently only hydra job advers I think..
10099 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10101 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10104 cw = Math.max(cw, this.totalWidth);
10105 this.getGridEl().select('tbody tr',true).setWidth(cw);
10108 // resize 'expandable coloumn?
10110 return; // we doe not have a view in this design..
10113 onBodyScroll: function()
10115 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10117 this.headEl.setStyle({
10118 'position' : 'relative',
10119 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10125 var scrollHeight = this.bodyEl.dom.scrollHeight;
10127 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10129 var height = this.bodyEl.getHeight();
10131 if(scrollHeight - height == scrollTop) {
10133 var total = this.ds.getTotalCount();
10135 if(this.footer.cursor + this.footer.pageSize < total){
10137 this.footer.ds.load({
10139 start : this.footer.cursor + this.footer.pageSize,
10140 limit : this.footer.pageSize
10149 onColumnSplitterMoved : function(i, diff)
10151 this.userResized = true;
10153 var cm = this.colModel;
10155 var w = this.getHeaderIndex(i).getWidth() + diff;
10158 cm.setColumnWidth(i, w, true);
10160 //var cid = cm.getColumnId(i); << not used in this version?
10161 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10163 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10164 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10165 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10167 //this.updateSplitters();
10168 //this.layout(); << ??
10169 this.fireEvent("columnresize", i, w);
10171 onHeaderChange : function()
10173 var header = this.renderHeader();
10174 var table = this.el.select('table', true).first();
10176 this.headEl.remove();
10177 this.headEl = table.createChild(header, this.bodyEl, false);
10179 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10180 e.on('click', this.sort, this);
10183 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10184 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10189 onHiddenChange : function(colModel, colIndex, hidden)
10192 this.cm.setHidden()
10193 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10194 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10196 this.CSS.updateRule(thSelector, "display", "");
10197 this.CSS.updateRule(tdSelector, "display", "");
10200 this.CSS.updateRule(thSelector, "display", "none");
10201 this.CSS.updateRule(tdSelector, "display", "none");
10204 // onload calls initCSS()
10205 this.onHeaderChange();
10209 setColumnWidth: function(col_index, width)
10211 // width = "md-2 xs-2..."
10212 if(!this.colModel.config[col_index]) {
10216 var w = width.split(" ");
10218 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10220 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10223 for(var j = 0; j < w.length; j++) {
10229 var size_cls = w[j].split("-");
10231 if(!Number.isInteger(size_cls[1] * 1)) {
10235 if(!this.colModel.config[col_index][size_cls[0]]) {
10239 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10243 h_row[0].classList.replace(
10244 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10245 "col-"+size_cls[0]+"-"+size_cls[1]
10248 for(var i = 0; i < rows.length; i++) {
10250 var size_cls = w[j].split("-");
10252 if(!Number.isInteger(size_cls[1] * 1)) {
10256 if(!this.colModel.config[col_index][size_cls[0]]) {
10260 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10264 rows[i].classList.replace(
10265 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10266 "col-"+size_cls[0]+"-"+size_cls[1]
10270 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10275 // currently only used to find the split on drag..
10276 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10281 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10282 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10291 * @class Roo.bootstrap.TableCell
10292 * @extends Roo.bootstrap.Component
10293 * Bootstrap TableCell class
10294 * @cfg {String} html cell contain text
10295 * @cfg {String} cls cell class
10296 * @cfg {String} tag cell tag (td|th) default td
10297 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10298 * @cfg {String} align Aligns the content in a cell
10299 * @cfg {String} axis Categorizes cells
10300 * @cfg {String} bgcolor Specifies the background color of a cell
10301 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10302 * @cfg {Number} colspan Specifies the number of columns a cell should span
10303 * @cfg {String} headers Specifies one or more header cells a cell is related to
10304 * @cfg {Number} height Sets the height of a cell
10305 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10306 * @cfg {Number} rowspan Sets the number of rows a cell should span
10307 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10308 * @cfg {String} valign Vertical aligns the content in a cell
10309 * @cfg {Number} width Specifies the width of a cell
10312 * Create a new TableCell
10313 * @param {Object} config The config object
10316 Roo.bootstrap.TableCell = function(config){
10317 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10320 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10340 getAutoCreate : function(){
10341 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10348 cfg.tag = this.tag;
10361 cfg.align=this.align
10366 if (this.bgcolor) {
10367 cfg.bgcolor=this.bgcolor
10369 if (this.charoff) {
10370 cfg.charoff=this.charoff
10372 if (this.colspan) {
10373 cfg.colspan=this.colspan
10375 if (this.headers) {
10376 cfg.headers=this.headers
10379 cfg.height=this.height
10382 cfg.nowrap=this.nowrap
10384 if (this.rowspan) {
10385 cfg.rowspan=this.rowspan
10388 cfg.scope=this.scope
10391 cfg.valign=this.valign
10394 cfg.width=this.width
10413 * @class Roo.bootstrap.TableRow
10414 * @extends Roo.bootstrap.Component
10415 * Bootstrap TableRow class
10416 * @cfg {String} cls row class
10417 * @cfg {String} align Aligns the content in a table row
10418 * @cfg {String} bgcolor Specifies a background color for a table row
10419 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10420 * @cfg {String} valign Vertical aligns the content in a table row
10423 * Create a new TableRow
10424 * @param {Object} config The config object
10427 Roo.bootstrap.TableRow = function(config){
10428 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10431 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10439 getAutoCreate : function(){
10440 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10447 cfg.cls = this.cls;
10450 cfg.align = this.align;
10453 cfg.bgcolor = this.bgcolor;
10456 cfg.charoff = this.charoff;
10459 cfg.valign = this.valign;
10477 * @class Roo.bootstrap.TableBody
10478 * @extends Roo.bootstrap.Component
10479 * Bootstrap TableBody class
10480 * @cfg {String} cls element class
10481 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10482 * @cfg {String} align Aligns the content inside the element
10483 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10484 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10487 * Create a new TableBody
10488 * @param {Object} config The config object
10491 Roo.bootstrap.TableBody = function(config){
10492 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10495 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10503 getAutoCreate : function(){
10504 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10514 cfg.tag = this.tag;
10518 cfg.align = this.align;
10521 cfg.charoff = this.charoff;
10524 cfg.valign = this.valign;
10531 // initEvents : function()
10534 // if(!this.store){
10538 // this.store = Roo.factory(this.store, Roo.data);
10539 // this.store.on('load', this.onLoad, this);
10541 // this.store.load();
10545 // onLoad: function ()
10547 // this.fireEvent('load', this);
10557 * Ext JS Library 1.1.1
10558 * Copyright(c) 2006-2007, Ext JS, LLC.
10560 * Originally Released Under LGPL - original licence link has changed is not relivant.
10563 * <script type="text/javascript">
10566 // as we use this in bootstrap.
10567 Roo.namespace('Roo.form');
10569 * @class Roo.form.Action
10570 * Internal Class used to handle form actions
10572 * @param {Roo.form.BasicForm} el The form element or its id
10573 * @param {Object} config Configuration options
10578 // define the action interface
10579 Roo.form.Action = function(form, options){
10581 this.options = options || {};
10584 * Client Validation Failed
10587 Roo.form.Action.CLIENT_INVALID = 'client';
10589 * Server Validation Failed
10592 Roo.form.Action.SERVER_INVALID = 'server';
10594 * Connect to Server Failed
10597 Roo.form.Action.CONNECT_FAILURE = 'connect';
10599 * Reading Data from Server Failed
10602 Roo.form.Action.LOAD_FAILURE = 'load';
10604 Roo.form.Action.prototype = {
10606 failureType : undefined,
10607 response : undefined,
10608 result : undefined,
10610 // interface method
10611 run : function(options){
10615 // interface method
10616 success : function(response){
10620 // interface method
10621 handleResponse : function(response){
10625 // default connection failure
10626 failure : function(response){
10628 this.response = response;
10629 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10630 this.form.afterAction(this, false);
10633 processResponse : function(response){
10634 this.response = response;
10635 if(!response.responseText){
10638 this.result = this.handleResponse(response);
10639 return this.result;
10642 // utility functions used internally
10643 getUrl : function(appendParams){
10644 var url = this.options.url || this.form.url || this.form.el.dom.action;
10646 var p = this.getParams();
10648 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10654 getMethod : function(){
10655 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10658 getParams : function(){
10659 var bp = this.form.baseParams;
10660 var p = this.options.params;
10662 if(typeof p == "object"){
10663 p = Roo.urlEncode(Roo.applyIf(p, bp));
10664 }else if(typeof p == 'string' && bp){
10665 p += '&' + Roo.urlEncode(bp);
10668 p = Roo.urlEncode(bp);
10673 createCallback : function(){
10675 success: this.success,
10676 failure: this.failure,
10678 timeout: (this.form.timeout*1000),
10679 upload: this.form.fileUpload ? this.success : undefined
10684 Roo.form.Action.Submit = function(form, options){
10685 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10688 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10691 haveProgress : false,
10692 uploadComplete : false,
10694 // uploadProgress indicator.
10695 uploadProgress : function()
10697 if (!this.form.progressUrl) {
10701 if (!this.haveProgress) {
10702 Roo.MessageBox.progress("Uploading", "Uploading");
10704 if (this.uploadComplete) {
10705 Roo.MessageBox.hide();
10709 this.haveProgress = true;
10711 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10713 var c = new Roo.data.Connection();
10715 url : this.form.progressUrl,
10720 success : function(req){
10721 //console.log(data);
10725 rdata = Roo.decode(req.responseText)
10727 Roo.log("Invalid data from server..");
10731 if (!rdata || !rdata.success) {
10733 Roo.MessageBox.alert(Roo.encode(rdata));
10736 var data = rdata.data;
10738 if (this.uploadComplete) {
10739 Roo.MessageBox.hide();
10744 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10745 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10748 this.uploadProgress.defer(2000,this);
10751 failure: function(data) {
10752 Roo.log('progress url failed ');
10763 // run get Values on the form, so it syncs any secondary forms.
10764 this.form.getValues();
10766 var o = this.options;
10767 var method = this.getMethod();
10768 var isPost = method == 'POST';
10769 if(o.clientValidation === false || this.form.isValid()){
10771 if (this.form.progressUrl) {
10772 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10773 (new Date() * 1) + '' + Math.random());
10778 Roo.Ajax.request(Roo.apply(this.createCallback(), {
10779 form:this.form.el.dom,
10780 url:this.getUrl(!isPost),
10782 params:isPost ? this.getParams() : null,
10783 isUpload: this.form.fileUpload,
10784 formData : this.form.formData
10787 this.uploadProgress();
10789 }else if (o.clientValidation !== false){ // client validation failed
10790 this.failureType = Roo.form.Action.CLIENT_INVALID;
10791 this.form.afterAction(this, false);
10795 success : function(response)
10797 this.uploadComplete= true;
10798 if (this.haveProgress) {
10799 Roo.MessageBox.hide();
10803 var result = this.processResponse(response);
10804 if(result === true || result.success){
10805 this.form.afterAction(this, true);
10809 this.form.markInvalid(result.errors);
10810 this.failureType = Roo.form.Action.SERVER_INVALID;
10812 this.form.afterAction(this, false);
10814 failure : function(response)
10816 this.uploadComplete= true;
10817 if (this.haveProgress) {
10818 Roo.MessageBox.hide();
10821 this.response = response;
10822 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10823 this.form.afterAction(this, false);
10826 handleResponse : function(response){
10827 if(this.form.errorReader){
10828 var rs = this.form.errorReader.read(response);
10831 for(var i = 0, len = rs.records.length; i < len; i++) {
10832 var r = rs.records[i];
10833 errors[i] = r.data;
10836 if(errors.length < 1){
10840 success : rs.success,
10846 ret = Roo.decode(response.responseText);
10850 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10860 Roo.form.Action.Load = function(form, options){
10861 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10862 this.reader = this.form.reader;
10865 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10870 Roo.Ajax.request(Roo.apply(
10871 this.createCallback(), {
10872 method:this.getMethod(),
10873 url:this.getUrl(false),
10874 params:this.getParams()
10878 success : function(response){
10880 var result = this.processResponse(response);
10881 if(result === true || !result.success || !result.data){
10882 this.failureType = Roo.form.Action.LOAD_FAILURE;
10883 this.form.afterAction(this, false);
10886 this.form.clearInvalid();
10887 this.form.setValues(result.data);
10888 this.form.afterAction(this, true);
10891 handleResponse : function(response){
10892 if(this.form.reader){
10893 var rs = this.form.reader.read(response);
10894 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10896 success : rs.success,
10900 return Roo.decode(response.responseText);
10904 Roo.form.Action.ACTION_TYPES = {
10905 'load' : Roo.form.Action.Load,
10906 'submit' : Roo.form.Action.Submit
10915 * @class Roo.bootstrap.Form
10916 * @extends Roo.bootstrap.Component
10917 * Bootstrap Form class
10918 * @cfg {String} method GET | POST (default POST)
10919 * @cfg {String} labelAlign top | left (default top)
10920 * @cfg {String} align left | right - for navbars
10921 * @cfg {Boolean} loadMask load mask when submit (default true)
10925 * Create a new Form
10926 * @param {Object} config The config object
10930 Roo.bootstrap.Form = function(config){
10932 Roo.bootstrap.Form.superclass.constructor.call(this, config);
10934 Roo.bootstrap.Form.popover.apply();
10938 * @event clientvalidation
10939 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10940 * @param {Form} this
10941 * @param {Boolean} valid true if the form has passed client-side validation
10943 clientvalidation: true,
10945 * @event beforeaction
10946 * Fires before any action is performed. Return false to cancel the action.
10947 * @param {Form} this
10948 * @param {Action} action The action to be performed
10950 beforeaction: true,
10952 * @event actionfailed
10953 * Fires when an action fails.
10954 * @param {Form} this
10955 * @param {Action} action The action that failed
10957 actionfailed : true,
10959 * @event actioncomplete
10960 * Fires when an action is completed.
10961 * @param {Form} this
10962 * @param {Action} action The action that completed
10964 actioncomplete : true
10968 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
10971 * @cfg {String} method
10972 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10976 * @cfg {String} url
10977 * The URL to use for form actions if one isn't supplied in the action options.
10980 * @cfg {Boolean} fileUpload
10981 * Set to true if this form is a file upload.
10985 * @cfg {Object} baseParams
10986 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10990 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10994 * @cfg {Sting} align (left|right) for navbar forms
10999 activeAction : null,
11002 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11003 * element by passing it or its id or mask the form itself by passing in true.
11006 waitMsgTarget : false,
11011 * @cfg {Boolean} errorMask (true|false) default false
11016 * @cfg {Number} maskOffset Default 100
11021 * @cfg {Boolean} maskBody
11025 getAutoCreate : function(){
11029 method : this.method || 'POST',
11030 id : this.id || Roo.id(),
11033 if (this.parent().xtype.match(/^Nav/)) {
11034 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11038 if (this.labelAlign == 'left' ) {
11039 cfg.cls += ' form-horizontal';
11045 initEvents : function()
11047 this.el.on('submit', this.onSubmit, this);
11048 // this was added as random key presses on the form where triggering form submit.
11049 this.el.on('keypress', function(e) {
11050 if (e.getCharCode() != 13) {
11053 // we might need to allow it for textareas.. and some other items.
11054 // check e.getTarget().
11056 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11060 Roo.log("keypress blocked");
11062 e.preventDefault();
11068 onSubmit : function(e){
11073 * Returns true if client-side validation on the form is successful.
11076 isValid : function(){
11077 var items = this.getItems();
11079 var target = false;
11081 items.each(function(f){
11087 Roo.log('invalid field: ' + f.name);
11091 if(!target && f.el.isVisible(true)){
11097 if(this.errorMask && !valid){
11098 Roo.bootstrap.Form.popover.mask(this, target);
11105 * Returns true if any fields in this form have changed since their original load.
11108 isDirty : function(){
11110 var items = this.getItems();
11111 items.each(function(f){
11121 * Performs a predefined action (submit or load) or custom actions you define on this form.
11122 * @param {String} actionName The name of the action type
11123 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11124 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11125 * accept other config options):
11127 Property Type Description
11128 ---------------- --------------- ----------------------------------------------------------------------------------
11129 url String The url for the action (defaults to the form's url)
11130 method String The form method to use (defaults to the form's method, or POST if not defined)
11131 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11132 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11133 validate the form on the client (defaults to false)
11135 * @return {BasicForm} this
11137 doAction : function(action, options){
11138 if(typeof action == 'string'){
11139 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11141 if(this.fireEvent('beforeaction', this, action) !== false){
11142 this.beforeAction(action);
11143 action.run.defer(100, action);
11149 beforeAction : function(action){
11150 var o = action.options;
11155 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11157 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11160 // not really supported yet.. ??
11162 //if(this.waitMsgTarget === true){
11163 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11164 //}else if(this.waitMsgTarget){
11165 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11166 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11168 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11174 afterAction : function(action, success){
11175 this.activeAction = null;
11176 var o = action.options;
11181 Roo.get(document.body).unmask();
11187 //if(this.waitMsgTarget === true){
11188 // this.el.unmask();
11189 //}else if(this.waitMsgTarget){
11190 // this.waitMsgTarget.unmask();
11192 // Roo.MessageBox.updateProgress(1);
11193 // Roo.MessageBox.hide();
11200 Roo.callback(o.success, o.scope, [this, action]);
11201 this.fireEvent('actioncomplete', this, action);
11205 // failure condition..
11206 // we have a scenario where updates need confirming.
11207 // eg. if a locking scenario exists..
11208 // we look for { errors : { needs_confirm : true }} in the response.
11210 (typeof(action.result) != 'undefined') &&
11211 (typeof(action.result.errors) != 'undefined') &&
11212 (typeof(action.result.errors.needs_confirm) != 'undefined')
11215 Roo.log("not supported yet");
11218 Roo.MessageBox.confirm(
11219 "Change requires confirmation",
11220 action.result.errorMsg,
11225 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11235 Roo.callback(o.failure, o.scope, [this, action]);
11236 // show an error message if no failed handler is set..
11237 if (!this.hasListener('actionfailed')) {
11238 Roo.log("need to add dialog support");
11240 Roo.MessageBox.alert("Error",
11241 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11242 action.result.errorMsg :
11243 "Saving Failed, please check your entries or try again"
11248 this.fireEvent('actionfailed', this, action);
11253 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11254 * @param {String} id The value to search for
11257 findField : function(id){
11258 var items = this.getItems();
11259 var field = items.get(id);
11261 items.each(function(f){
11262 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11269 return field || null;
11272 * Mark fields in this form invalid in bulk.
11273 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11274 * @return {BasicForm} this
11276 markInvalid : function(errors){
11277 if(errors instanceof Array){
11278 for(var i = 0, len = errors.length; i < len; i++){
11279 var fieldError = errors[i];
11280 var f = this.findField(fieldError.id);
11282 f.markInvalid(fieldError.msg);
11288 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11289 field.markInvalid(errors[id]);
11293 //Roo.each(this.childForms || [], function (f) {
11294 // f.markInvalid(errors);
11301 * Set values for fields in this form in bulk.
11302 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11303 * @return {BasicForm} this
11305 setValues : function(values){
11306 if(values instanceof Array){ // array of objects
11307 for(var i = 0, len = values.length; i < len; i++){
11309 var f = this.findField(v.id);
11311 f.setValue(v.value);
11312 if(this.trackResetOnLoad){
11313 f.originalValue = f.getValue();
11317 }else{ // object hash
11320 if(typeof values[id] != 'function' && (field = this.findField(id))){
11322 if (field.setFromData &&
11323 field.valueField &&
11324 field.displayField &&
11325 // combos' with local stores can
11326 // be queried via setValue()
11327 // to set their value..
11328 (field.store && !field.store.isLocal)
11332 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11333 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11334 field.setFromData(sd);
11336 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11338 field.setFromData(values);
11341 field.setValue(values[id]);
11345 if(this.trackResetOnLoad){
11346 field.originalValue = field.getValue();
11352 //Roo.each(this.childForms || [], function (f) {
11353 // f.setValues(values);
11360 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11361 * they are returned as an array.
11362 * @param {Boolean} asString
11365 getValues : function(asString){
11366 //if (this.childForms) {
11367 // copy values from the child forms
11368 // Roo.each(this.childForms, function (f) {
11369 // this.setValues(f.getValues());
11375 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11376 if(asString === true){
11379 return Roo.urlDecode(fs);
11383 * Returns the fields in this form as an object with key/value pairs.
11384 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11387 getFieldValues : function(with_hidden)
11389 var items = this.getItems();
11391 items.each(function(f){
11393 if (!f.getName()) {
11397 var v = f.getValue();
11399 if (f.inputType =='radio') {
11400 if (typeof(ret[f.getName()]) == 'undefined') {
11401 ret[f.getName()] = ''; // empty..
11404 if (!f.el.dom.checked) {
11408 v = f.el.dom.value;
11412 if(f.xtype == 'MoneyField'){
11413 ret[f.currencyName] = f.getCurrency();
11416 // not sure if this supported any more..
11417 if ((typeof(v) == 'object') && f.getRawValue) {
11418 v = f.getRawValue() ; // dates..
11420 // combo boxes where name != hiddenName...
11421 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11422 ret[f.name] = f.getRawValue();
11424 ret[f.getName()] = v;
11431 * Clears all invalid messages in this form.
11432 * @return {BasicForm} this
11434 clearInvalid : function(){
11435 var items = this.getItems();
11437 items.each(function(f){
11445 * Resets this form.
11446 * @return {BasicForm} this
11448 reset : function(){
11449 var items = this.getItems();
11450 items.each(function(f){
11454 Roo.each(this.childForms || [], function (f) {
11462 getItems : function()
11464 var r=new Roo.util.MixedCollection(false, function(o){
11465 return o.id || (o.id = Roo.id());
11467 var iter = function(el) {
11474 Roo.each(el.items,function(e) {
11483 hideFields : function(items)
11485 Roo.each(items, function(i){
11487 var f = this.findField(i);
11498 showFields : function(items)
11500 Roo.each(items, function(i){
11502 var f = this.findField(i);
11515 Roo.apply(Roo.bootstrap.Form, {
11531 intervalID : false,
11537 if(this.isApplied){
11542 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11543 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11544 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11545 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11548 this.maskEl.top.enableDisplayMode("block");
11549 this.maskEl.left.enableDisplayMode("block");
11550 this.maskEl.bottom.enableDisplayMode("block");
11551 this.maskEl.right.enableDisplayMode("block");
11553 this.toolTip = new Roo.bootstrap.Tooltip({
11554 cls : 'roo-form-error-popover',
11556 'left' : ['r-l', [-2,0], 'right'],
11557 'right' : ['l-r', [2,0], 'left'],
11558 'bottom' : ['tl-bl', [0,2], 'top'],
11559 'top' : [ 'bl-tl', [0,-2], 'bottom']
11563 this.toolTip.render(Roo.get(document.body));
11565 this.toolTip.el.enableDisplayMode("block");
11567 Roo.get(document.body).on('click', function(){
11571 Roo.get(document.body).on('touchstart', function(){
11575 this.isApplied = true
11578 mask : function(form, target)
11582 this.target = target;
11584 if(!this.form.errorMask || !target.el){
11588 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11590 Roo.log(scrollable);
11592 var ot = this.target.el.calcOffsetsTo(scrollable);
11594 var scrollTo = ot[1] - this.form.maskOffset;
11596 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11598 scrollable.scrollTo('top', scrollTo);
11600 var box = this.target.el.getBox();
11602 var zIndex = Roo.bootstrap.Modal.zIndex++;
11605 this.maskEl.top.setStyle('position', 'absolute');
11606 this.maskEl.top.setStyle('z-index', zIndex);
11607 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11608 this.maskEl.top.setLeft(0);
11609 this.maskEl.top.setTop(0);
11610 this.maskEl.top.show();
11612 this.maskEl.left.setStyle('position', 'absolute');
11613 this.maskEl.left.setStyle('z-index', zIndex);
11614 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11615 this.maskEl.left.setLeft(0);
11616 this.maskEl.left.setTop(box.y - this.padding);
11617 this.maskEl.left.show();
11619 this.maskEl.bottom.setStyle('position', 'absolute');
11620 this.maskEl.bottom.setStyle('z-index', zIndex);
11621 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11622 this.maskEl.bottom.setLeft(0);
11623 this.maskEl.bottom.setTop(box.bottom + this.padding);
11624 this.maskEl.bottom.show();
11626 this.maskEl.right.setStyle('position', 'absolute');
11627 this.maskEl.right.setStyle('z-index', zIndex);
11628 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11629 this.maskEl.right.setLeft(box.right + this.padding);
11630 this.maskEl.right.setTop(box.y - this.padding);
11631 this.maskEl.right.show();
11633 this.toolTip.bindEl = this.target.el;
11635 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11637 var tip = this.target.blankText;
11639 if(this.target.getValue() !== '' ) {
11641 if (this.target.invalidText.length) {
11642 tip = this.target.invalidText;
11643 } else if (this.target.regexText.length){
11644 tip = this.target.regexText;
11648 this.toolTip.show(tip);
11650 this.intervalID = window.setInterval(function() {
11651 Roo.bootstrap.Form.popover.unmask();
11654 window.onwheel = function(){ return false;};
11656 (function(){ this.isMasked = true; }).defer(500, this);
11660 unmask : function()
11662 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11666 this.maskEl.top.setStyle('position', 'absolute');
11667 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11668 this.maskEl.top.hide();
11670 this.maskEl.left.setStyle('position', 'absolute');
11671 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11672 this.maskEl.left.hide();
11674 this.maskEl.bottom.setStyle('position', 'absolute');
11675 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11676 this.maskEl.bottom.hide();
11678 this.maskEl.right.setStyle('position', 'absolute');
11679 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11680 this.maskEl.right.hide();
11682 this.toolTip.hide();
11684 this.toolTip.el.hide();
11686 window.onwheel = function(){ return true;};
11688 if(this.intervalID){
11689 window.clearInterval(this.intervalID);
11690 this.intervalID = false;
11693 this.isMasked = false;
11703 * Ext JS Library 1.1.1
11704 * Copyright(c) 2006-2007, Ext JS, LLC.
11706 * Originally Released Under LGPL - original licence link has changed is not relivant.
11709 * <script type="text/javascript">
11712 * @class Roo.form.VTypes
11713 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11716 Roo.form.VTypes = function(){
11717 // closure these in so they are only created once.
11718 var alpha = /^[a-zA-Z_]+$/;
11719 var alphanum = /^[a-zA-Z0-9_]+$/;
11720 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11721 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11723 // All these messages and functions are configurable
11726 * The function used to validate email addresses
11727 * @param {String} value The email address
11729 'email' : function(v){
11730 return email.test(v);
11733 * The error text to display when the email validation function returns false
11736 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11738 * The keystroke filter mask to be applied on email input
11741 'emailMask' : /[a-z0-9_\.\-@]/i,
11744 * The function used to validate URLs
11745 * @param {String} value The URL
11747 'url' : function(v){
11748 return url.test(v);
11751 * The error text to display when the url validation function returns false
11754 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11757 * The function used to validate alpha values
11758 * @param {String} value The value
11760 'alpha' : function(v){
11761 return alpha.test(v);
11764 * The error text to display when the alpha validation function returns false
11767 'alphaText' : 'This field should only contain letters and _',
11769 * The keystroke filter mask to be applied on alpha input
11772 'alphaMask' : /[a-z_]/i,
11775 * The function used to validate alphanumeric values
11776 * @param {String} value The value
11778 'alphanum' : function(v){
11779 return alphanum.test(v);
11782 * The error text to display when the alphanumeric validation function returns false
11785 'alphanumText' : 'This field should only contain letters, numbers and _',
11787 * The keystroke filter mask to be applied on alphanumeric input
11790 'alphanumMask' : /[a-z0-9_]/i
11800 * @class Roo.bootstrap.Input
11801 * @extends Roo.bootstrap.Component
11802 * Bootstrap Input class
11803 * @cfg {Boolean} disabled is it disabled
11804 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
11805 * @cfg {String} name name of the input
11806 * @cfg {string} fieldLabel - the label associated
11807 * @cfg {string} placeholder - placeholder to put in text.
11808 * @cfg {string} before - input group add on before
11809 * @cfg {string} after - input group add on after
11810 * @cfg {string} size - (lg|sm) or leave empty..
11811 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11812 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11813 * @cfg {Number} md colspan out of 12 for computer-sized screens
11814 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11815 * @cfg {string} value default value of the input
11816 * @cfg {Number} labelWidth set the width of label
11817 * @cfg {Number} labellg set the width of label (1-12)
11818 * @cfg {Number} labelmd set the width of label (1-12)
11819 * @cfg {Number} labelsm set the width of label (1-12)
11820 * @cfg {Number} labelxs set the width of label (1-12)
11821 * @cfg {String} labelAlign (top|left)
11822 * @cfg {Boolean} readOnly Specifies that the field should be read-only
11823 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11824 * @cfg {String} indicatorpos (left|right) default left
11825 * @cfg {String} capture (user|camera) use for file input only. (default empty)
11826 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11827 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11829 * @cfg {String} align (left|center|right) Default left
11830 * @cfg {Boolean} forceFeedback (true|false) Default false
11833 * Create a new Input
11834 * @param {Object} config The config object
11837 Roo.bootstrap.Input = function(config){
11839 Roo.bootstrap.Input.superclass.constructor.call(this, config);
11844 * Fires when this field receives input focus.
11845 * @param {Roo.form.Field} this
11850 * Fires when this field loses input focus.
11851 * @param {Roo.form.Field} this
11855 * @event specialkey
11856 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
11857 * {@link Roo.EventObject#getKey} to determine which key was pressed.
11858 * @param {Roo.form.Field} this
11859 * @param {Roo.EventObject} e The event object
11864 * Fires just before the field blurs if the field value has changed.
11865 * @param {Roo.form.Field} this
11866 * @param {Mixed} newValue The new value
11867 * @param {Mixed} oldValue The original value
11872 * Fires after the field has been marked as invalid.
11873 * @param {Roo.form.Field} this
11874 * @param {String} msg The validation message
11879 * Fires after the field has been validated with no errors.
11880 * @param {Roo.form.Field} this
11885 * Fires after the key up
11886 * @param {Roo.form.Field} this
11887 * @param {Roo.EventObject} e The event Object
11892 * Fires after the user pastes into input
11893 * @param {Roo.form.Field} this
11894 * @param {Roo.EventObject} e The event Object
11900 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
11902 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11903 automatic validation (defaults to "keyup").
11905 validationEvent : "keyup",
11907 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11909 validateOnBlur : true,
11911 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11913 validationDelay : 250,
11915 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11917 focusClass : "x-form-focus", // not needed???
11921 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11923 invalidClass : "has-warning",
11926 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11928 validClass : "has-success",
11931 * @cfg {Boolean} hasFeedback (true|false) default true
11933 hasFeedback : true,
11936 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11938 invalidFeedbackClass : "glyphicon-warning-sign",
11941 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11943 validFeedbackClass : "glyphicon-ok",
11946 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11948 selectOnFocus : false,
11951 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11955 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11960 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11962 disableKeyFilter : false,
11965 * @cfg {Boolean} disabled True to disable the field (defaults to false).
11969 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11973 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11975 blankText : "Please complete this mandatory field",
11978 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11982 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11984 maxLength : Number.MAX_VALUE,
11986 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11988 minLengthText : "The minimum length for this field is {0}",
11990 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11992 maxLengthText : "The maximum length for this field is {0}",
11996 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11997 * If available, this function will be called only after the basic validators all return true, and will be passed the
11998 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12002 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12003 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12004 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
12008 * @cfg {String} regexText -- Depricated - use Invalid Text
12013 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12019 autocomplete: false,
12023 inputType : 'text',
12026 placeholder: false,
12031 preventMark: false,
12032 isFormField : true,
12035 labelAlign : false,
12038 formatedValue : false,
12039 forceFeedback : false,
12041 indicatorpos : 'left',
12051 parentLabelAlign : function()
12054 while (parent.parent()) {
12055 parent = parent.parent();
12056 if (typeof(parent.labelAlign) !='undefined') {
12057 return parent.labelAlign;
12064 getAutoCreate : function()
12066 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12072 if(this.inputType != 'hidden'){
12073 cfg.cls = 'form-group' //input-group
12079 type : this.inputType,
12080 value : this.value,
12081 cls : 'form-control',
12082 placeholder : this.placeholder || '',
12083 autocomplete : this.autocomplete || 'new-password'
12085 if (this.inputType == 'file') {
12086 input.style = 'overflow:hidden'; // why not in CSS?
12089 if(this.capture.length){
12090 input.capture = this.capture;
12093 if(this.accept.length){
12094 input.accept = this.accept + "/*";
12098 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12101 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12102 input.maxLength = this.maxLength;
12105 if (this.disabled) {
12106 input.disabled=true;
12109 if (this.readOnly) {
12110 input.readonly=true;
12114 input.name = this.name;
12118 input.cls += ' input-' + this.size;
12122 ['xs','sm','md','lg'].map(function(size){
12123 if (settings[size]) {
12124 cfg.cls += ' col-' + size + '-' + settings[size];
12128 var inputblock = input;
12132 cls: 'glyphicon form-control-feedback'
12135 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12138 cls : 'has-feedback',
12146 if (this.before || this.after) {
12149 cls : 'input-group',
12153 if (this.before && typeof(this.before) == 'string') {
12155 inputblock.cn.push({
12157 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12161 if (this.before && typeof(this.before) == 'object') {
12162 this.before = Roo.factory(this.before);
12164 inputblock.cn.push({
12166 cls : 'roo-input-before input-group-prepend input-group-' +
12167 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12171 inputblock.cn.push(input);
12173 if (this.after && typeof(this.after) == 'string') {
12174 inputblock.cn.push({
12176 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12180 if (this.after && typeof(this.after) == 'object') {
12181 this.after = Roo.factory(this.after);
12183 inputblock.cn.push({
12185 cls : 'roo-input-after input-group-append input-group-' +
12186 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12190 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12191 inputblock.cls += ' has-feedback';
12192 inputblock.cn.push(feedback);
12197 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12198 tooltip : 'This field is required'
12200 if (this.allowBlank ) {
12201 indicator.style = this.allowBlank ? ' display:none' : '';
12203 if (align ==='left' && this.fieldLabel.length) {
12205 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12212 cls : 'control-label col-form-label',
12213 html : this.fieldLabel
12224 var labelCfg = cfg.cn[1];
12225 var contentCfg = cfg.cn[2];
12227 if(this.indicatorpos == 'right'){
12232 cls : 'control-label col-form-label',
12236 html : this.fieldLabel
12250 labelCfg = cfg.cn[0];
12251 contentCfg = cfg.cn[1];
12255 if(this.labelWidth > 12){
12256 labelCfg.style = "width: " + this.labelWidth + 'px';
12259 if(this.labelWidth < 13 && this.labelmd == 0){
12260 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12263 if(this.labellg > 0){
12264 labelCfg.cls += ' col-lg-' + this.labellg;
12265 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12268 if(this.labelmd > 0){
12269 labelCfg.cls += ' col-md-' + this.labelmd;
12270 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12273 if(this.labelsm > 0){
12274 labelCfg.cls += ' col-sm-' + this.labelsm;
12275 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12278 if(this.labelxs > 0){
12279 labelCfg.cls += ' col-xs-' + this.labelxs;
12280 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12284 } else if ( this.fieldLabel.length) {
12291 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12292 tooltip : 'This field is required',
12293 style : this.allowBlank ? ' display:none' : ''
12297 //cls : 'input-group-addon',
12298 html : this.fieldLabel
12306 if(this.indicatorpos == 'right'){
12311 //cls : 'input-group-addon',
12312 html : this.fieldLabel
12317 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12318 tooltip : 'This field is required',
12319 style : this.allowBlank ? ' display:none' : ''
12339 if (this.parentType === 'Navbar' && this.parent().bar) {
12340 cfg.cls += ' navbar-form';
12343 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12344 // on BS4 we do this only if not form
12345 cfg.cls += ' navbar-form';
12353 * return the real input element.
12355 inputEl: function ()
12357 return this.el.select('input.form-control',true).first();
12360 tooltipEl : function()
12362 return this.inputEl();
12365 indicatorEl : function()
12367 if (Roo.bootstrap.version == 4) {
12368 return false; // not enabled in v4 yet.
12371 var indicator = this.el.select('i.roo-required-indicator',true).first();
12381 setDisabled : function(v)
12383 var i = this.inputEl().dom;
12385 i.removeAttribute('disabled');
12389 i.setAttribute('disabled','true');
12391 initEvents : function()
12394 this.inputEl().on("keydown" , this.fireKey, this);
12395 this.inputEl().on("focus", this.onFocus, this);
12396 this.inputEl().on("blur", this.onBlur, this);
12398 this.inputEl().relayEvent('keyup', this);
12399 this.inputEl().relayEvent('paste', this);
12401 this.indicator = this.indicatorEl();
12403 if(this.indicator){
12404 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12407 // reference to original value for reset
12408 this.originalValue = this.getValue();
12409 //Roo.form.TextField.superclass.initEvents.call(this);
12410 if(this.validationEvent == 'keyup'){
12411 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12412 this.inputEl().on('keyup', this.filterValidation, this);
12414 else if(this.validationEvent !== false){
12415 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12418 if(this.selectOnFocus){
12419 this.on("focus", this.preFocus, this);
12422 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12423 this.inputEl().on("keypress", this.filterKeys, this);
12425 this.inputEl().relayEvent('keypress', this);
12428 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12429 this.el.on("click", this.autoSize, this);
12432 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12433 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12436 if (typeof(this.before) == 'object') {
12437 this.before.render(this.el.select('.roo-input-before',true).first());
12439 if (typeof(this.after) == 'object') {
12440 this.after.render(this.el.select('.roo-input-after',true).first());
12443 this.inputEl().on('change', this.onChange, this);
12446 filterValidation : function(e){
12447 if(!e.isNavKeyPress()){
12448 this.validationTask.delay(this.validationDelay);
12452 * Validates the field value
12453 * @return {Boolean} True if the value is valid, else false
12455 validate : function(){
12456 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12457 if(this.disabled || this.validateValue(this.getRawValue())){
12462 this.markInvalid();
12468 * Validates a value according to the field's validation rules and marks the field as invalid
12469 * if the validation fails
12470 * @param {Mixed} value The value to validate
12471 * @return {Boolean} True if the value is valid, else false
12473 validateValue : function(value)
12475 if(this.getVisibilityEl().hasClass('hidden')){
12479 if(value.length < 1) { // if it's blank
12480 if(this.allowBlank){
12486 if(value.length < this.minLength){
12489 if(value.length > this.maxLength){
12493 var vt = Roo.form.VTypes;
12494 if(!vt[this.vtype](value, this)){
12498 if(typeof this.validator == "function"){
12499 var msg = this.validator(value);
12503 if (typeof(msg) == 'string') {
12504 this.invalidText = msg;
12508 if(this.regex && !this.regex.test(value)){
12516 fireKey : function(e){
12517 //Roo.log('field ' + e.getKey());
12518 if(e.isNavKeyPress()){
12519 this.fireEvent("specialkey", this, e);
12522 focus : function (selectText){
12524 this.inputEl().focus();
12525 if(selectText === true){
12526 this.inputEl().dom.select();
12532 onFocus : function(){
12533 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12534 // this.el.addClass(this.focusClass);
12536 if(!this.hasFocus){
12537 this.hasFocus = true;
12538 this.startValue = this.getValue();
12539 this.fireEvent("focus", this);
12543 beforeBlur : Roo.emptyFn,
12547 onBlur : function(){
12549 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12550 //this.el.removeClass(this.focusClass);
12552 this.hasFocus = false;
12553 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12556 var v = this.getValue();
12557 if(String(v) !== String(this.startValue)){
12558 this.fireEvent('change', this, v, this.startValue);
12560 this.fireEvent("blur", this);
12563 onChange : function(e)
12565 var v = this.getValue();
12566 if(String(v) !== String(this.startValue)){
12567 this.fireEvent('change', this, v, this.startValue);
12573 * Resets the current field value to the originally loaded value and clears any validation messages
12575 reset : function(){
12576 this.setValue(this.originalValue);
12580 * Returns the name of the field
12581 * @return {Mixed} name The name field
12583 getName: function(){
12587 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12588 * @return {Mixed} value The field value
12590 getValue : function(){
12592 var v = this.inputEl().getValue();
12597 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
12598 * @return {Mixed} value The field value
12600 getRawValue : function(){
12601 var v = this.inputEl().getValue();
12607 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
12608 * @param {Mixed} value The value to set
12610 setRawValue : function(v){
12611 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12614 selectText : function(start, end){
12615 var v = this.getRawValue();
12617 start = start === undefined ? 0 : start;
12618 end = end === undefined ? v.length : end;
12619 var d = this.inputEl().dom;
12620 if(d.setSelectionRange){
12621 d.setSelectionRange(start, end);
12622 }else if(d.createTextRange){
12623 var range = d.createTextRange();
12624 range.moveStart("character", start);
12625 range.moveEnd("character", v.length-end);
12632 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
12633 * @param {Mixed} value The value to set
12635 setValue : function(v){
12638 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12644 processValue : function(value){
12645 if(this.stripCharsRe){
12646 var newValue = value.replace(this.stripCharsRe, '');
12647 if(newValue !== value){
12648 this.setRawValue(newValue);
12655 preFocus : function(){
12657 if(this.selectOnFocus){
12658 this.inputEl().dom.select();
12661 filterKeys : function(e){
12662 var k = e.getKey();
12663 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12666 var c = e.getCharCode(), cc = String.fromCharCode(c);
12667 if(Roo.isIE && (e.isSpecialKey() || !cc)){
12670 if(!this.maskRe.test(cc)){
12675 * Clear any invalid styles/messages for this field
12677 clearInvalid : function(){
12679 if(!this.el || this.preventMark){ // not rendered
12684 this.el.removeClass([this.invalidClass, 'is-invalid']);
12686 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12688 var feedback = this.el.select('.form-control-feedback', true).first();
12691 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12696 if(this.indicator){
12697 this.indicator.removeClass('visible');
12698 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12701 this.fireEvent('valid', this);
12705 * Mark this field as valid
12707 markValid : function()
12709 if(!this.el || this.preventMark){ // not rendered...
12713 this.el.removeClass([this.invalidClass, this.validClass]);
12714 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12716 var feedback = this.el.select('.form-control-feedback', true).first();
12719 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12722 if(this.indicator){
12723 this.indicator.removeClass('visible');
12724 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12732 if(this.allowBlank && !this.getRawValue().length){
12735 if (Roo.bootstrap.version == 3) {
12736 this.el.addClass(this.validClass);
12738 this.inputEl().addClass('is-valid');
12741 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12743 var feedback = this.el.select('.form-control-feedback', true).first();
12746 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12747 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12752 this.fireEvent('valid', this);
12756 * Mark this field as invalid
12757 * @param {String} msg The validation message
12759 markInvalid : function(msg)
12761 if(!this.el || this.preventMark){ // not rendered
12765 this.el.removeClass([this.invalidClass, this.validClass]);
12766 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12768 var feedback = this.el.select('.form-control-feedback', true).first();
12771 this.el.select('.form-control-feedback', true).first().removeClass(
12772 [this.invalidFeedbackClass, this.validFeedbackClass]);
12779 if(this.allowBlank && !this.getRawValue().length){
12783 if(this.indicator){
12784 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12785 this.indicator.addClass('visible');
12787 if (Roo.bootstrap.version == 3) {
12788 this.el.addClass(this.invalidClass);
12790 this.inputEl().addClass('is-invalid');
12795 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12797 var feedback = this.el.select('.form-control-feedback', true).first();
12800 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12802 if(this.getValue().length || this.forceFeedback){
12803 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12810 this.fireEvent('invalid', this, msg);
12813 SafariOnKeyDown : function(event)
12815 // this is a workaround for a password hang bug on chrome/ webkit.
12816 if (this.inputEl().dom.type != 'password') {
12820 var isSelectAll = false;
12822 if(this.inputEl().dom.selectionEnd > 0){
12823 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12825 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12826 event.preventDefault();
12831 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12833 event.preventDefault();
12834 // this is very hacky as keydown always get's upper case.
12836 var cc = String.fromCharCode(event.getCharCode());
12837 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
12841 adjustWidth : function(tag, w){
12842 tag = tag.toLowerCase();
12843 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12844 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12845 if(tag == 'input'){
12848 if(tag == 'textarea'){
12851 }else if(Roo.isOpera){
12852 if(tag == 'input'){
12855 if(tag == 'textarea'){
12863 setFieldLabel : function(v)
12865 if(!this.rendered){
12869 if(this.indicatorEl()){
12870 var ar = this.el.select('label > span',true);
12872 if (ar.elements.length) {
12873 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12874 this.fieldLabel = v;
12878 var br = this.el.select('label',true);
12880 if(br.elements.length) {
12881 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12882 this.fieldLabel = v;
12886 Roo.log('Cannot Found any of label > span || label in input');
12890 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12891 this.fieldLabel = v;
12906 * @class Roo.bootstrap.TextArea
12907 * @extends Roo.bootstrap.Input
12908 * Bootstrap TextArea class
12909 * @cfg {Number} cols Specifies the visible width of a text area
12910 * @cfg {Number} rows Specifies the visible number of lines in a text area
12911 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12912 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12913 * @cfg {string} html text
12916 * Create a new TextArea
12917 * @param {Object} config The config object
12920 Roo.bootstrap.TextArea = function(config){
12921 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12925 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
12935 getAutoCreate : function(){
12937 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12943 if(this.inputType != 'hidden'){
12944 cfg.cls = 'form-group' //input-group
12952 value : this.value || '',
12953 html: this.html || '',
12954 cls : 'form-control',
12955 placeholder : this.placeholder || ''
12959 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12960 input.maxLength = this.maxLength;
12964 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12968 input.cols = this.cols;
12971 if (this.readOnly) {
12972 input.readonly = true;
12976 input.name = this.name;
12980 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12984 ['xs','sm','md','lg'].map(function(size){
12985 if (settings[size]) {
12986 cfg.cls += ' col-' + size + '-' + settings[size];
12990 var inputblock = input;
12992 if(this.hasFeedback && !this.allowBlank){
12996 cls: 'glyphicon form-control-feedback'
13000 cls : 'has-feedback',
13009 if (this.before || this.after) {
13012 cls : 'input-group',
13016 inputblock.cn.push({
13018 cls : 'input-group-addon',
13023 inputblock.cn.push(input);
13025 if(this.hasFeedback && !this.allowBlank){
13026 inputblock.cls += ' has-feedback';
13027 inputblock.cn.push(feedback);
13031 inputblock.cn.push({
13033 cls : 'input-group-addon',
13040 if (align ==='left' && this.fieldLabel.length) {
13045 cls : 'control-label',
13046 html : this.fieldLabel
13057 if(this.labelWidth > 12){
13058 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13061 if(this.labelWidth < 13 && this.labelmd == 0){
13062 this.labelmd = this.labelWidth;
13065 if(this.labellg > 0){
13066 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13067 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13070 if(this.labelmd > 0){
13071 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13072 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13075 if(this.labelsm > 0){
13076 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13077 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13080 if(this.labelxs > 0){
13081 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13082 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13085 } else if ( this.fieldLabel.length) {
13090 //cls : 'input-group-addon',
13091 html : this.fieldLabel
13109 if (this.disabled) {
13110 input.disabled=true;
13117 * return the real textarea element.
13119 inputEl: function ()
13121 return this.el.select('textarea.form-control',true).first();
13125 * Clear any invalid styles/messages for this field
13127 clearInvalid : function()
13130 if(!this.el || this.preventMark){ // not rendered
13134 var label = this.el.select('label', true).first();
13135 var icon = this.el.select('i.fa-star', true).first();
13140 this.el.removeClass( this.validClass);
13141 this.inputEl().removeClass('is-invalid');
13143 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13145 var feedback = this.el.select('.form-control-feedback', true).first();
13148 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13153 this.fireEvent('valid', this);
13157 * Mark this field as valid
13159 markValid : function()
13161 if(!this.el || this.preventMark){ // not rendered
13165 this.el.removeClass([this.invalidClass, this.validClass]);
13166 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13168 var feedback = this.el.select('.form-control-feedback', true).first();
13171 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13174 if(this.disabled || this.allowBlank){
13178 var label = this.el.select('label', true).first();
13179 var icon = this.el.select('i.fa-star', true).first();
13184 if (Roo.bootstrap.version == 3) {
13185 this.el.addClass(this.validClass);
13187 this.inputEl().addClass('is-valid');
13191 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13193 var feedback = this.el.select('.form-control-feedback', true).first();
13196 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13197 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13202 this.fireEvent('valid', this);
13206 * Mark this field as invalid
13207 * @param {String} msg The validation message
13209 markInvalid : function(msg)
13211 if(!this.el || this.preventMark){ // not rendered
13215 this.el.removeClass([this.invalidClass, this.validClass]);
13216 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13218 var feedback = this.el.select('.form-control-feedback', true).first();
13221 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13224 if(this.disabled || this.allowBlank){
13228 var label = this.el.select('label', true).first();
13229 var icon = this.el.select('i.fa-star', true).first();
13231 if(!this.getValue().length && label && !icon){
13232 this.el.createChild({
13234 cls : 'text-danger fa fa-lg fa-star',
13235 tooltip : 'This field is required',
13236 style : 'margin-right:5px;'
13240 if (Roo.bootstrap.version == 3) {
13241 this.el.addClass(this.invalidClass);
13243 this.inputEl().addClass('is-invalid');
13246 // fixme ... this may be depricated need to test..
13247 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13249 var feedback = this.el.select('.form-control-feedback', true).first();
13252 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13254 if(this.getValue().length || this.forceFeedback){
13255 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13262 this.fireEvent('invalid', this, msg);
13270 * trigger field - base class for combo..
13275 * @class Roo.bootstrap.TriggerField
13276 * @extends Roo.bootstrap.Input
13277 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13278 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13279 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13280 * for which you can provide a custom implementation. For example:
13282 var trigger = new Roo.bootstrap.TriggerField();
13283 trigger.onTriggerClick = myTriggerFn;
13284 trigger.applyTo('my-field');
13287 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13288 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13289 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13290 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13291 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13294 * Create a new TriggerField.
13295 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13296 * to the base TextField)
13298 Roo.bootstrap.TriggerField = function(config){
13299 this.mimicing = false;
13300 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13303 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
13305 * @cfg {String} triggerClass A CSS class to apply to the trigger
13308 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13313 * @cfg {Boolean} removable (true|false) special filter default false
13317 /** @cfg {Boolean} grow @hide */
13318 /** @cfg {Number} growMin @hide */
13319 /** @cfg {Number} growMax @hide */
13325 autoSize: Roo.emptyFn,
13329 deferHeight : true,
13332 actionMode : 'wrap',
13337 getAutoCreate : function(){
13339 var align = this.labelAlign || this.parentLabelAlign();
13344 cls: 'form-group' //input-group
13351 type : this.inputType,
13352 cls : 'form-control',
13353 autocomplete: 'new-password',
13354 placeholder : this.placeholder || ''
13358 input.name = this.name;
13361 input.cls += ' input-' + this.size;
13364 if (this.disabled) {
13365 input.disabled=true;
13368 var inputblock = input;
13370 if(this.hasFeedback && !this.allowBlank){
13374 cls: 'glyphicon form-control-feedback'
13377 if(this.removable && !this.editable ){
13379 cls : 'has-feedback',
13385 cls : 'roo-combo-removable-btn close'
13392 cls : 'has-feedback',
13401 if(this.removable && !this.editable ){
13403 cls : 'roo-removable',
13409 cls : 'roo-combo-removable-btn close'
13416 if (this.before || this.after) {
13419 cls : 'input-group',
13423 inputblock.cn.push({
13425 cls : 'input-group-addon input-group-prepend input-group-text',
13430 inputblock.cn.push(input);
13432 if(this.hasFeedback && !this.allowBlank){
13433 inputblock.cls += ' has-feedback';
13434 inputblock.cn.push(feedback);
13438 inputblock.cn.push({
13440 cls : 'input-group-addon input-group-append input-group-text',
13449 var ibwrap = inputblock;
13454 cls: 'roo-select2-choices',
13458 cls: 'roo-select2-search-field',
13470 cls: 'roo-select2-container input-group',
13475 cls: 'form-hidden-field'
13481 if(!this.multiple && this.showToggleBtn){
13487 if (this.caret != false) {
13490 cls: 'fa fa-' + this.caret
13497 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13499 Roo.bootstrap.version == 3 ? caret : '',
13502 cls: 'combobox-clear',
13516 combobox.cls += ' roo-select2-container-multi';
13520 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13521 tooltip : 'This field is required'
13523 if (Roo.bootstrap.version == 4) {
13526 style : 'display:none'
13531 if (align ==='left' && this.fieldLabel.length) {
13533 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13540 cls : 'control-label',
13541 html : this.fieldLabel
13553 var labelCfg = cfg.cn[1];
13554 var contentCfg = cfg.cn[2];
13556 if(this.indicatorpos == 'right'){
13561 cls : 'control-label',
13565 html : this.fieldLabel
13579 labelCfg = cfg.cn[0];
13580 contentCfg = cfg.cn[1];
13583 if(this.labelWidth > 12){
13584 labelCfg.style = "width: " + this.labelWidth + 'px';
13587 if(this.labelWidth < 13 && this.labelmd == 0){
13588 this.labelmd = this.labelWidth;
13591 if(this.labellg > 0){
13592 labelCfg.cls += ' col-lg-' + this.labellg;
13593 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13596 if(this.labelmd > 0){
13597 labelCfg.cls += ' col-md-' + this.labelmd;
13598 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13601 if(this.labelsm > 0){
13602 labelCfg.cls += ' col-sm-' + this.labelsm;
13603 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13606 if(this.labelxs > 0){
13607 labelCfg.cls += ' col-xs-' + this.labelxs;
13608 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13611 } else if ( this.fieldLabel.length) {
13612 // Roo.log(" label");
13617 //cls : 'input-group-addon',
13618 html : this.fieldLabel
13626 if(this.indicatorpos == 'right'){
13634 html : this.fieldLabel
13648 // Roo.log(" no label && no align");
13655 ['xs','sm','md','lg'].map(function(size){
13656 if (settings[size]) {
13657 cfg.cls += ' col-' + size + '-' + settings[size];
13668 onResize : function(w, h){
13669 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13670 // if(typeof w == 'number'){
13671 // var x = w - this.trigger.getWidth();
13672 // this.inputEl().setWidth(this.adjustWidth('input', x));
13673 // this.trigger.setStyle('left', x+'px');
13678 adjustSize : Roo.BoxComponent.prototype.adjustSize,
13681 getResizeEl : function(){
13682 return this.inputEl();
13686 getPositionEl : function(){
13687 return this.inputEl();
13691 alignErrorIcon : function(){
13692 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13696 initEvents : function(){
13700 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13701 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13702 if(!this.multiple && this.showToggleBtn){
13703 this.trigger = this.el.select('span.dropdown-toggle',true).first();
13704 if(this.hideTrigger){
13705 this.trigger.setDisplayed(false);
13707 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13711 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13714 if(this.removable && !this.editable && !this.tickable){
13715 var close = this.closeTriggerEl();
13718 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13719 close.on('click', this.removeBtnClick, this, close);
13723 //this.trigger.addClassOnOver('x-form-trigger-over');
13724 //this.trigger.addClassOnClick('x-form-trigger-click');
13727 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13731 closeTriggerEl : function()
13733 var close = this.el.select('.roo-combo-removable-btn', true).first();
13734 return close ? close : false;
13737 removeBtnClick : function(e, h, el)
13739 e.preventDefault();
13741 if(this.fireEvent("remove", this) !== false){
13743 this.fireEvent("afterremove", this)
13747 createList : function()
13749 this.list = Roo.get(document.body).createChild({
13750 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13751 cls: 'typeahead typeahead-long dropdown-menu shadow',
13752 style: 'display:none'
13755 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13760 initTrigger : function(){
13765 onDestroy : function(){
13767 this.trigger.removeAllListeners();
13768 // this.trigger.remove();
13771 // this.wrap.remove();
13773 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13777 onFocus : function(){
13778 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13780 if(!this.mimicing){
13781 this.wrap.addClass('x-trigger-wrap-focus');
13782 this.mimicing = true;
13783 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13784 if(this.monitorTab){
13785 this.el.on("keydown", this.checkTab, this);
13792 checkTab : function(e){
13793 if(e.getKey() == e.TAB){
13794 this.triggerBlur();
13799 onBlur : function(){
13804 mimicBlur : function(e, t){
13806 if(!this.wrap.contains(t) && this.validateBlur()){
13807 this.triggerBlur();
13813 triggerBlur : function(){
13814 this.mimicing = false;
13815 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13816 if(this.monitorTab){
13817 this.el.un("keydown", this.checkTab, this);
13819 //this.wrap.removeClass('x-trigger-wrap-focus');
13820 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13824 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13825 validateBlur : function(e, t){
13830 onDisable : function(){
13831 this.inputEl().dom.disabled = true;
13832 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13834 // this.wrap.addClass('x-item-disabled');
13839 onEnable : function(){
13840 this.inputEl().dom.disabled = false;
13841 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13843 // this.el.removeClass('x-item-disabled');
13848 onShow : function(){
13849 var ae = this.getActionEl();
13852 ae.dom.style.display = '';
13853 ae.dom.style.visibility = 'visible';
13859 onHide : function(){
13860 var ae = this.getActionEl();
13861 ae.dom.style.display = 'none';
13865 * The function that should handle the trigger's click event. This method does nothing by default until overridden
13866 * by an implementing function.
13868 * @param {EventObject} e
13870 onTriggerClick : Roo.emptyFn
13878 * @class Roo.bootstrap.CardUploader
13879 * @extends Roo.bootstrap.Button
13880 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13881 * @cfg {Number} errorTimeout default 3000
13882 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
13883 * @cfg {Array} html The button text.
13887 * Create a new CardUploader
13888 * @param {Object} config The config object
13891 Roo.bootstrap.CardUploader = function(config){
13895 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13898 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
13906 * When a image is clicked on - and needs to display a slideshow or similar..
13907 * @param {Roo.bootstrap.Card} this
13908 * @param {Object} The image information data
13914 * When a the download link is clicked
13915 * @param {Roo.bootstrap.Card} this
13916 * @param {Object} The image information data contains
13923 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
13926 errorTimeout : 3000,
13930 fileCollection : false,
13933 getAutoCreate : function()
13937 cls :'form-group' ,
13942 //cls : 'input-group-addon',
13943 html : this.fieldLabel
13951 value : this.value,
13952 cls : 'd-none form-control'
13957 multiple : 'multiple',
13959 cls : 'd-none roo-card-upload-selector'
13963 cls : 'roo-card-uploader-button-container w-100 mb-2'
13966 cls : 'card-columns roo-card-uploader-container'
13976 getChildContainer : function() /// what children are added to.
13978 return this.containerEl;
13981 getButtonContainer : function() /// what children are added to.
13983 return this.el.select(".roo-card-uploader-button-container").first();
13986 initEvents : function()
13989 Roo.bootstrap.Input.prototype.initEvents.call(this);
13993 xns: Roo.bootstrap,
13996 container_method : 'getButtonContainer' ,
13997 html : this.html, // fix changable?
14000 'click' : function(btn, e) {
14009 this.urlAPI = (window.createObjectURL && window) ||
14010 (window.URL && URL.revokeObjectURL && URL) ||
14011 (window.webkitURL && webkitURL);
14016 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14018 this.selectorEl.on('change', this.onFileSelected, this);
14021 this.images.forEach(function(img) {
14024 this.images = false;
14026 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14032 onClick : function(e)
14034 e.preventDefault();
14036 this.selectorEl.dom.click();
14040 onFileSelected : function(e)
14042 e.preventDefault();
14044 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14048 Roo.each(this.selectorEl.dom.files, function(file){
14049 this.addFile(file);
14058 addFile : function(file)
14061 if(typeof(file) === 'string'){
14062 throw "Add file by name?"; // should not happen
14066 if(!file || !this.urlAPI){
14076 var url = _this.urlAPI.createObjectURL( file);
14079 id : Roo.bootstrap.CardUploader.ID--,
14080 is_uploaded : false,
14084 mimetype : file.type,
14092 * addCard - add an Attachment to the uploader
14093 * @param data - the data about the image to upload
14097 title : "Title of file",
14098 is_uploaded : false,
14099 src : "http://.....",
14100 srcfile : { the File upload object },
14101 mimetype : file.type,
14104 .. any other data...
14110 addCard : function (data)
14112 // hidden input element?
14113 // if the file is not an image...
14114 //then we need to use something other that and header_image
14119 xns : Roo.bootstrap,
14120 xtype : 'CardFooter',
14123 xns : Roo.bootstrap,
14129 xns : Roo.bootstrap,
14131 html : String.format("<small>{0}</small>", data.title),
14132 cls : 'col-10 text-left',
14137 click : function() {
14139 t.fireEvent( "download", t, data );
14145 xns : Roo.bootstrap,
14147 style: 'max-height: 28px; ',
14153 click : function() {
14154 t.removeCard(data.id)
14166 var cn = this.addxtype(
14169 xns : Roo.bootstrap,
14172 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14173 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14174 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14179 initEvents : function() {
14180 Roo.bootstrap.Card.prototype.initEvents.call(this);
14182 this.imgEl = this.el.select('.card-img-top').first();
14184 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14185 this.imgEl.set({ 'pointer' : 'cursor' });
14188 this.getCardFooter().addClass('p-1');
14195 // dont' really need ot update items.
14196 // this.items.push(cn);
14197 this.fileCollection.add(cn);
14199 if (!data.srcfile) {
14200 this.updateInput();
14205 var reader = new FileReader();
14206 reader.addEventListener("load", function() {
14207 data.srcdata = reader.result;
14210 reader.readAsDataURL(data.srcfile);
14215 removeCard : function(id)
14218 var card = this.fileCollection.get(id);
14219 card.data.is_deleted = 1;
14220 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14221 //this.fileCollection.remove(card);
14222 //this.items = this.items.filter(function(e) { return e != card });
14223 // dont' really need ot update items.
14224 card.el.dom.parentNode.removeChild(card.el.dom);
14225 this.updateInput();
14231 this.fileCollection.each(function(card) {
14232 if (card.el.dom && card.el.dom.parentNode) {
14233 card.el.dom.parentNode.removeChild(card.el.dom);
14236 this.fileCollection.clear();
14237 this.updateInput();
14240 updateInput : function()
14243 this.fileCollection.each(function(e) {
14247 this.inputEl().dom.value = JSON.stringify(data);
14257 Roo.bootstrap.CardUploader.ID = -1;/*
14259 * Ext JS Library 1.1.1
14260 * Copyright(c) 2006-2007, Ext JS, LLC.
14262 * Originally Released Under LGPL - original licence link has changed is not relivant.
14265 * <script type="text/javascript">
14270 * @class Roo.data.SortTypes
14272 * Defines the default sorting (casting?) comparison functions used when sorting data.
14274 Roo.data.SortTypes = {
14276 * Default sort that does nothing
14277 * @param {Mixed} s The value being converted
14278 * @return {Mixed} The comparison value
14280 none : function(s){
14285 * The regular expression used to strip tags
14289 stripTagsRE : /<\/?[^>]+>/gi,
14292 * Strips all HTML tags to sort on text only
14293 * @param {Mixed} s The value being converted
14294 * @return {String} The comparison value
14296 asText : function(s){
14297 return String(s).replace(this.stripTagsRE, "");
14301 * Strips all HTML tags to sort on text only - Case insensitive
14302 * @param {Mixed} s The value being converted
14303 * @return {String} The comparison value
14305 asUCText : function(s){
14306 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14310 * Case insensitive string
14311 * @param {Mixed} s The value being converted
14312 * @return {String} The comparison value
14314 asUCString : function(s) {
14315 return String(s).toUpperCase();
14320 * @param {Mixed} s The value being converted
14321 * @return {Number} The comparison value
14323 asDate : function(s) {
14327 if(s instanceof Date){
14328 return s.getTime();
14330 return Date.parse(String(s));
14335 * @param {Mixed} s The value being converted
14336 * @return {Float} The comparison value
14338 asFloat : function(s) {
14339 var val = parseFloat(String(s).replace(/,/g, ""));
14348 * @param {Mixed} s The value being converted
14349 * @return {Number} The comparison value
14351 asInt : function(s) {
14352 var val = parseInt(String(s).replace(/,/g, ""));
14360 * Ext JS Library 1.1.1
14361 * Copyright(c) 2006-2007, Ext JS, LLC.
14363 * Originally Released Under LGPL - original licence link has changed is not relivant.
14366 * <script type="text/javascript">
14370 * @class Roo.data.Record
14371 * Instances of this class encapsulate both record <em>definition</em> information, and record
14372 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14373 * to access Records cached in an {@link Roo.data.Store} object.<br>
14375 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14376 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14379 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14381 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14382 * {@link #create}. The parameters are the same.
14383 * @param {Array} data An associative Array of data values keyed by the field name.
14384 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14385 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14386 * not specified an integer id is generated.
14388 Roo.data.Record = function(data, id){
14389 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14394 * Generate a constructor for a specific record layout.
14395 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14396 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14397 * Each field definition object may contain the following properties: <ul>
14398 * <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,
14399 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14400 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14401 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14402 * is being used, then this is a string containing the javascript expression to reference the data relative to
14403 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14404 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14405 * this may be omitted.</p></li>
14406 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14407 * <ul><li>auto (Default, implies no conversion)</li>
14412 * <li>date</li></ul></p></li>
14413 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14414 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14415 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14416 * by the Reader into an object that will be stored in the Record. It is passed the
14417 * following parameters:<ul>
14418 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14420 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14422 * <br>usage:<br><pre><code>
14423 var TopicRecord = Roo.data.Record.create(
14424 {name: 'title', mapping: 'topic_title'},
14425 {name: 'author', mapping: 'username'},
14426 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14427 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14428 {name: 'lastPoster', mapping: 'user2'},
14429 {name: 'excerpt', mapping: 'post_text'}
14432 var myNewRecord = new TopicRecord({
14433 title: 'Do my job please',
14436 lastPost: new Date(),
14437 lastPoster: 'Animal',
14438 excerpt: 'No way dude!'
14440 myStore.add(myNewRecord);
14445 Roo.data.Record.create = function(o){
14446 var f = function(){
14447 f.superclass.constructor.apply(this, arguments);
14449 Roo.extend(f, Roo.data.Record);
14450 var p = f.prototype;
14451 p.fields = new Roo.util.MixedCollection(false, function(field){
14454 for(var i = 0, len = o.length; i < len; i++){
14455 p.fields.add(new Roo.data.Field(o[i]));
14457 f.getField = function(name){
14458 return p.fields.get(name);
14463 Roo.data.Record.AUTO_ID = 1000;
14464 Roo.data.Record.EDIT = 'edit';
14465 Roo.data.Record.REJECT = 'reject';
14466 Roo.data.Record.COMMIT = 'commit';
14468 Roo.data.Record.prototype = {
14470 * Readonly flag - true if this record has been modified.
14479 join : function(store){
14480 this.store = store;
14484 * Set the named field to the specified value.
14485 * @param {String} name The name of the field to set.
14486 * @param {Object} value The value to set the field to.
14488 set : function(name, value){
14489 if(this.data[name] == value){
14493 if(!this.modified){
14494 this.modified = {};
14496 if(typeof this.modified[name] == 'undefined'){
14497 this.modified[name] = this.data[name];
14499 this.data[name] = value;
14500 if(!this.editing && this.store){
14501 this.store.afterEdit(this);
14506 * Get the value of the named field.
14507 * @param {String} name The name of the field to get the value of.
14508 * @return {Object} The value of the field.
14510 get : function(name){
14511 return this.data[name];
14515 beginEdit : function(){
14516 this.editing = true;
14517 this.modified = {};
14521 cancelEdit : function(){
14522 this.editing = false;
14523 delete this.modified;
14527 endEdit : function(){
14528 this.editing = false;
14529 if(this.dirty && this.store){
14530 this.store.afterEdit(this);
14535 * Usually called by the {@link Roo.data.Store} which owns the Record.
14536 * Rejects all changes made to the Record since either creation, or the last commit operation.
14537 * Modified fields are reverted to their original values.
14539 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14540 * of reject operations.
14542 reject : function(){
14543 var m = this.modified;
14545 if(typeof m[n] != "function"){
14546 this.data[n] = m[n];
14549 this.dirty = false;
14550 delete this.modified;
14551 this.editing = false;
14553 this.store.afterReject(this);
14558 * Usually called by the {@link Roo.data.Store} which owns the Record.
14559 * Commits all changes made to the Record since either creation, or the last commit operation.
14561 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14562 * of commit operations.
14564 commit : function(){
14565 this.dirty = false;
14566 delete this.modified;
14567 this.editing = false;
14569 this.store.afterCommit(this);
14574 hasError : function(){
14575 return this.error != null;
14579 clearError : function(){
14584 * Creates a copy of this record.
14585 * @param {String} id (optional) A new record id if you don't want to use this record's id
14588 copy : function(newId) {
14589 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14593 * Ext JS Library 1.1.1
14594 * Copyright(c) 2006-2007, Ext JS, LLC.
14596 * Originally Released Under LGPL - original licence link has changed is not relivant.
14599 * <script type="text/javascript">
14605 * @class Roo.data.Store
14606 * @extends Roo.util.Observable
14607 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14608 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14610 * 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
14611 * has no knowledge of the format of the data returned by the Proxy.<br>
14613 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14614 * instances from the data object. These records are cached and made available through accessor functions.
14616 * Creates a new Store.
14617 * @param {Object} config A config object containing the objects needed for the Store to access data,
14618 * and read the data into Records.
14620 Roo.data.Store = function(config){
14621 this.data = new Roo.util.MixedCollection(false);
14622 this.data.getKey = function(o){
14625 this.baseParams = {};
14627 this.paramNames = {
14632 "multisort" : "_multisort"
14635 if(config && config.data){
14636 this.inlineData = config.data;
14637 delete config.data;
14640 Roo.apply(this, config);
14642 if(this.reader){ // reader passed
14643 this.reader = Roo.factory(this.reader, Roo.data);
14644 this.reader.xmodule = this.xmodule || false;
14645 if(!this.recordType){
14646 this.recordType = this.reader.recordType;
14648 if(this.reader.onMetaChange){
14649 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14653 if(this.recordType){
14654 this.fields = this.recordType.prototype.fields;
14656 this.modified = [];
14660 * @event datachanged
14661 * Fires when the data cache has changed, and a widget which is using this Store
14662 * as a Record cache should refresh its view.
14663 * @param {Store} this
14665 datachanged : true,
14667 * @event metachange
14668 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14669 * @param {Store} this
14670 * @param {Object} meta The JSON metadata
14675 * Fires when Records have been added to the Store
14676 * @param {Store} this
14677 * @param {Roo.data.Record[]} records The array of Records added
14678 * @param {Number} index The index at which the record(s) were added
14683 * Fires when a Record has been removed from the Store
14684 * @param {Store} this
14685 * @param {Roo.data.Record} record The Record that was removed
14686 * @param {Number} index The index at which the record was removed
14691 * Fires when a Record has been updated
14692 * @param {Store} this
14693 * @param {Roo.data.Record} record The Record that was updated
14694 * @param {String} operation The update operation being performed. Value may be one of:
14696 Roo.data.Record.EDIT
14697 Roo.data.Record.REJECT
14698 Roo.data.Record.COMMIT
14704 * Fires when the data cache has been cleared.
14705 * @param {Store} this
14709 * @event beforeload
14710 * Fires before a request is made for a new data object. If the beforeload handler returns false
14711 * the load action will be canceled.
14712 * @param {Store} this
14713 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14717 * @event beforeloadadd
14718 * Fires after a new set of Records has been loaded.
14719 * @param {Store} this
14720 * @param {Roo.data.Record[]} records The Records that were loaded
14721 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14723 beforeloadadd : true,
14726 * Fires after a new set of Records has been loaded, before they are added to the store.
14727 * @param {Store} this
14728 * @param {Roo.data.Record[]} records The Records that were loaded
14729 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14730 * @params {Object} return from reader
14734 * @event loadexception
14735 * Fires if an exception occurs in the Proxy during loading.
14736 * Called with the signature of the Proxy's "loadexception" event.
14737 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14740 * @param {Object} return from JsonData.reader() - success, totalRecords, records
14741 * @param {Object} load options
14742 * @param {Object} jsonData from your request (normally this contains the Exception)
14744 loadexception : true
14748 this.proxy = Roo.factory(this.proxy, Roo.data);
14749 this.proxy.xmodule = this.xmodule || false;
14750 this.relayEvents(this.proxy, ["loadexception"]);
14752 this.sortToggle = {};
14753 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14755 Roo.data.Store.superclass.constructor.call(this);
14757 if(this.inlineData){
14758 this.loadData(this.inlineData);
14759 delete this.inlineData;
14763 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14765 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
14766 * without a remote query - used by combo/forms at present.
14770 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
14773 * @cfg {Array} data Inline data to be loaded when the store is initialized.
14776 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
14777 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14780 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14781 * on any HTTP request
14784 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14787 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14791 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14792 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14794 remoteSort : false,
14797 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14798 * loaded or when a record is removed. (defaults to false).
14800 pruneModifiedRecords : false,
14803 lastOptions : null,
14806 * Add Records to the Store and fires the add event.
14807 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14809 add : function(records){
14810 records = [].concat(records);
14811 for(var i = 0, len = records.length; i < len; i++){
14812 records[i].join(this);
14814 var index = this.data.length;
14815 this.data.addAll(records);
14816 this.fireEvent("add", this, records, index);
14820 * Remove a Record from the Store and fires the remove event.
14821 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14823 remove : function(record){
14824 var index = this.data.indexOf(record);
14825 this.data.removeAt(index);
14827 if(this.pruneModifiedRecords){
14828 this.modified.remove(record);
14830 this.fireEvent("remove", this, record, index);
14834 * Remove all Records from the Store and fires the clear event.
14836 removeAll : function(){
14838 if(this.pruneModifiedRecords){
14839 this.modified = [];
14841 this.fireEvent("clear", this);
14845 * Inserts Records to the Store at the given index and fires the add event.
14846 * @param {Number} index The start index at which to insert the passed Records.
14847 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14849 insert : function(index, records){
14850 records = [].concat(records);
14851 for(var i = 0, len = records.length; i < len; i++){
14852 this.data.insert(index, records[i]);
14853 records[i].join(this);
14855 this.fireEvent("add", this, records, index);
14859 * Get the index within the cache of the passed Record.
14860 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14861 * @return {Number} The index of the passed Record. Returns -1 if not found.
14863 indexOf : function(record){
14864 return this.data.indexOf(record);
14868 * Get the index within the cache of the Record with the passed id.
14869 * @param {String} id The id of the Record to find.
14870 * @return {Number} The index of the Record. Returns -1 if not found.
14872 indexOfId : function(id){
14873 return this.data.indexOfKey(id);
14877 * Get the Record with the specified id.
14878 * @param {String} id The id of the Record to find.
14879 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14881 getById : function(id){
14882 return this.data.key(id);
14886 * Get the Record at the specified index.
14887 * @param {Number} index The index of the Record to find.
14888 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14890 getAt : function(index){
14891 return this.data.itemAt(index);
14895 * Returns a range of Records between specified indices.
14896 * @param {Number} startIndex (optional) The starting index (defaults to 0)
14897 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14898 * @return {Roo.data.Record[]} An array of Records
14900 getRange : function(start, end){
14901 return this.data.getRange(start, end);
14905 storeOptions : function(o){
14906 o = Roo.apply({}, o);
14909 this.lastOptions = o;
14913 * Loads the Record cache from the configured Proxy using the configured Reader.
14915 * If using remote paging, then the first load call must specify the <em>start</em>
14916 * and <em>limit</em> properties in the options.params property to establish the initial
14917 * position within the dataset, and the number of Records to cache on each read from the Proxy.
14919 * <strong>It is important to note that for remote data sources, loading is asynchronous,
14920 * and this call will return before the new data has been loaded. Perform any post-processing
14921 * in a callback function, or in a "load" event handler.</strong>
14923 * @param {Object} options An object containing properties which control loading options:<ul>
14924 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14925 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14926 * passed the following arguments:<ul>
14927 * <li>r : Roo.data.Record[]</li>
14928 * <li>options: Options object from the load call</li>
14929 * <li>success: Boolean success indicator</li></ul></li>
14930 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14931 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14934 load : function(options){
14935 options = options || {};
14936 if(this.fireEvent("beforeload", this, options) !== false){
14937 this.storeOptions(options);
14938 var p = Roo.apply(options.params || {}, this.baseParams);
14939 // if meta was not loaded from remote source.. try requesting it.
14940 if (!this.reader.metaFromRemote) {
14941 p._requestMeta = 1;
14943 if(this.sortInfo && this.remoteSort){
14944 var pn = this.paramNames;
14945 p[pn["sort"]] = this.sortInfo.field;
14946 p[pn["dir"]] = this.sortInfo.direction;
14948 if (this.multiSort) {
14949 var pn = this.paramNames;
14950 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14953 this.proxy.load(p, this.reader, this.loadRecords, this, options);
14958 * Reloads the Record cache from the configured Proxy using the configured Reader and
14959 * the options from the last load operation performed.
14960 * @param {Object} options (optional) An object containing properties which may override the options
14961 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14962 * the most recently used options are reused).
14964 reload : function(options){
14965 this.load(Roo.applyIf(options||{}, this.lastOptions));
14969 // Called as a callback by the Reader during a load operation.
14970 loadRecords : function(o, options, success){
14971 if(!o || success === false){
14972 if(success !== false){
14973 this.fireEvent("load", this, [], options, o);
14975 if(options.callback){
14976 options.callback.call(options.scope || this, [], options, false);
14980 // if data returned failure - throw an exception.
14981 if (o.success === false) {
14982 // show a message if no listener is registered.
14983 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14984 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14986 // loadmask wil be hooked into this..
14987 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14990 var r = o.records, t = o.totalRecords || r.length;
14992 this.fireEvent("beforeloadadd", this, r, options, o);
14994 if(!options || options.add !== true){
14995 if(this.pruneModifiedRecords){
14996 this.modified = [];
14998 for(var i = 0, len = r.length; i < len; i++){
15002 this.data = this.snapshot;
15003 delete this.snapshot;
15006 this.data.addAll(r);
15007 this.totalLength = t;
15009 this.fireEvent("datachanged", this);
15011 this.totalLength = Math.max(t, this.data.length+r.length);
15015 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15017 var e = new Roo.data.Record({});
15019 e.set(this.parent.displayField, this.parent.emptyTitle);
15020 e.set(this.parent.valueField, '');
15025 this.fireEvent("load", this, r, options, o);
15026 if(options.callback){
15027 options.callback.call(options.scope || this, r, options, true);
15033 * Loads data from a passed data block. A Reader which understands the format of the data
15034 * must have been configured in the constructor.
15035 * @param {Object} data The data block from which to read the Records. The format of the data expected
15036 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15037 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15039 loadData : function(o, append){
15040 var r = this.reader.readRecords(o);
15041 this.loadRecords(r, {add: append}, true);
15045 * using 'cn' the nested child reader read the child array into it's child stores.
15046 * @param {Object} rec The record with a 'children array
15048 loadDataFromChildren : function(rec)
15050 this.loadData(this.reader.toLoadData(rec));
15055 * Gets the number of cached records.
15057 * <em>If using paging, this may not be the total size of the dataset. If the data object
15058 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15059 * the data set size</em>
15061 getCount : function(){
15062 return this.data.length || 0;
15066 * Gets the total number of records in the dataset as returned by the server.
15068 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15069 * the dataset size</em>
15071 getTotalCount : function(){
15072 return this.totalLength || 0;
15076 * Returns the sort state of the Store as an object with two properties:
15078 field {String} The name of the field by which the Records are sorted
15079 direction {String} The sort order, "ASC" or "DESC"
15082 getSortState : function(){
15083 return this.sortInfo;
15087 applySort : function(){
15088 if(this.sortInfo && !this.remoteSort){
15089 var s = this.sortInfo, f = s.field;
15090 var st = this.fields.get(f).sortType;
15091 var fn = function(r1, r2){
15092 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15093 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15095 this.data.sort(s.direction, fn);
15096 if(this.snapshot && this.snapshot != this.data){
15097 this.snapshot.sort(s.direction, fn);
15103 * Sets the default sort column and order to be used by the next load operation.
15104 * @param {String} fieldName The name of the field to sort by.
15105 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15107 setDefaultSort : function(field, dir){
15108 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15112 * Sort the Records.
15113 * If remote sorting is used, the sort is performed on the server, and the cache is
15114 * reloaded. If local sorting is used, the cache is sorted internally.
15115 * @param {String} fieldName The name of the field to sort by.
15116 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15118 sort : function(fieldName, dir){
15119 var f = this.fields.get(fieldName);
15121 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15123 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15124 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15129 this.sortToggle[f.name] = dir;
15130 this.sortInfo = {field: f.name, direction: dir};
15131 if(!this.remoteSort){
15133 this.fireEvent("datachanged", this);
15135 this.load(this.lastOptions);
15140 * Calls the specified function for each of the Records in the cache.
15141 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15142 * Returning <em>false</em> aborts and exits the iteration.
15143 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15145 each : function(fn, scope){
15146 this.data.each(fn, scope);
15150 * Gets all records modified since the last commit. Modified records are persisted across load operations
15151 * (e.g., during paging).
15152 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15154 getModifiedRecords : function(){
15155 return this.modified;
15159 createFilterFn : function(property, value, anyMatch){
15160 if(!value.exec){ // not a regex
15161 value = String(value);
15162 if(value.length == 0){
15165 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15167 return function(r){
15168 return value.test(r.data[property]);
15173 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15174 * @param {String} property A field on your records
15175 * @param {Number} start The record index to start at (defaults to 0)
15176 * @param {Number} end The last record index to include (defaults to length - 1)
15177 * @return {Number} The sum
15179 sum : function(property, start, end){
15180 var rs = this.data.items, v = 0;
15181 start = start || 0;
15182 end = (end || end === 0) ? end : rs.length-1;
15184 for(var i = start; i <= end; i++){
15185 v += (rs[i].data[property] || 0);
15191 * Filter the records by a specified property.
15192 * @param {String} field A field on your records
15193 * @param {String/RegExp} value Either a string that the field
15194 * should start with or a RegExp to test against the field
15195 * @param {Boolean} anyMatch True to match any part not just the beginning
15197 filter : function(property, value, anyMatch){
15198 var fn = this.createFilterFn(property, value, anyMatch);
15199 return fn ? this.filterBy(fn) : this.clearFilter();
15203 * Filter by a function. The specified function will be called with each
15204 * record in this data source. If the function returns true the record is included,
15205 * otherwise it is filtered.
15206 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15207 * @param {Object} scope (optional) The scope of the function (defaults to this)
15209 filterBy : function(fn, scope){
15210 this.snapshot = this.snapshot || this.data;
15211 this.data = this.queryBy(fn, scope||this);
15212 this.fireEvent("datachanged", this);
15216 * Query the records by a specified property.
15217 * @param {String} field A field on your records
15218 * @param {String/RegExp} value Either a string that the field
15219 * should start with or a RegExp to test against the field
15220 * @param {Boolean} anyMatch True to match any part not just the beginning
15221 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15223 query : function(property, value, anyMatch){
15224 var fn = this.createFilterFn(property, value, anyMatch);
15225 return fn ? this.queryBy(fn) : this.data.clone();
15229 * Query by a function. The specified function will be called with each
15230 * record in this data source. If the function returns true the record is included
15232 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15233 * @param {Object} scope (optional) The scope of the function (defaults to this)
15234 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15236 queryBy : function(fn, scope){
15237 var data = this.snapshot || this.data;
15238 return data.filterBy(fn, scope||this);
15242 * Collects unique values for a particular dataIndex from this store.
15243 * @param {String} dataIndex The property to collect
15244 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15245 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15246 * @return {Array} An array of the unique values
15248 collect : function(dataIndex, allowNull, bypassFilter){
15249 var d = (bypassFilter === true && this.snapshot) ?
15250 this.snapshot.items : this.data.items;
15251 var v, sv, r = [], l = {};
15252 for(var i = 0, len = d.length; i < len; i++){
15253 v = d[i].data[dataIndex];
15255 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15264 * Revert to a view of the Record cache with no filtering applied.
15265 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15267 clearFilter : function(suppressEvent){
15268 if(this.snapshot && this.snapshot != this.data){
15269 this.data = this.snapshot;
15270 delete this.snapshot;
15271 if(suppressEvent !== true){
15272 this.fireEvent("datachanged", this);
15278 afterEdit : function(record){
15279 if(this.modified.indexOf(record) == -1){
15280 this.modified.push(record);
15282 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15286 afterReject : function(record){
15287 this.modified.remove(record);
15288 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15292 afterCommit : function(record){
15293 this.modified.remove(record);
15294 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15298 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15299 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15301 commitChanges : function(){
15302 var m = this.modified.slice(0);
15303 this.modified = [];
15304 for(var i = 0, len = m.length; i < len; i++){
15310 * Cancel outstanding changes on all changed records.
15312 rejectChanges : function(){
15313 var m = this.modified.slice(0);
15314 this.modified = [];
15315 for(var i = 0, len = m.length; i < len; i++){
15320 onMetaChange : function(meta, rtype, o){
15321 this.recordType = rtype;
15322 this.fields = rtype.prototype.fields;
15323 delete this.snapshot;
15324 this.sortInfo = meta.sortInfo || this.sortInfo;
15325 this.modified = [];
15326 this.fireEvent('metachange', this, this.reader.meta);
15329 moveIndex : function(data, type)
15331 var index = this.indexOf(data);
15333 var newIndex = index + type;
15337 this.insert(newIndex, data);
15342 * Ext JS Library 1.1.1
15343 * Copyright(c) 2006-2007, Ext JS, LLC.
15345 * Originally Released Under LGPL - original licence link has changed is not relivant.
15348 * <script type="text/javascript">
15352 * @class Roo.data.SimpleStore
15353 * @extends Roo.data.Store
15354 * Small helper class to make creating Stores from Array data easier.
15355 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15356 * @cfg {Array} fields An array of field definition objects, or field name strings.
15357 * @cfg {Object} an existing reader (eg. copied from another store)
15358 * @cfg {Array} data The multi-dimensional array of data
15359 * @cfg {Roo.data.DataProxy} proxy [not-required]
15360 * @cfg {Roo.data.Reader} reader [not-required]
15362 * @param {Object} config
15364 Roo.data.SimpleStore = function(config)
15366 Roo.data.SimpleStore.superclass.constructor.call(this, {
15368 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15371 Roo.data.Record.create(config.fields)
15373 proxy : new Roo.data.MemoryProxy(config.data)
15377 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15379 * Ext JS Library 1.1.1
15380 * Copyright(c) 2006-2007, Ext JS, LLC.
15382 * Originally Released Under LGPL - original licence link has changed is not relivant.
15385 * <script type="text/javascript">
15390 * @extends Roo.data.Store
15391 * @class Roo.data.JsonStore
15392 * Small helper class to make creating Stores for JSON data easier. <br/>
15394 var store = new Roo.data.JsonStore({
15395 url: 'get-images.php',
15397 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15400 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15401 * JsonReader and HttpProxy (unless inline data is provided).</b>
15402 * @cfg {Array} fields An array of field definition objects, or field name strings.
15404 * @param {Object} config
15406 Roo.data.JsonStore = function(c){
15407 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15408 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15409 reader: new Roo.data.JsonReader(c, c.fields)
15412 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15414 * Ext JS Library 1.1.1
15415 * Copyright(c) 2006-2007, Ext JS, LLC.
15417 * Originally Released Under LGPL - original licence link has changed is not relivant.
15420 * <script type="text/javascript">
15424 Roo.data.Field = function(config){
15425 if(typeof config == "string"){
15426 config = {name: config};
15428 Roo.apply(this, config);
15431 this.type = "auto";
15434 var st = Roo.data.SortTypes;
15435 // named sortTypes are supported, here we look them up
15436 if(typeof this.sortType == "string"){
15437 this.sortType = st[this.sortType];
15440 // set default sortType for strings and dates
15441 if(!this.sortType){
15444 this.sortType = st.asUCString;
15447 this.sortType = st.asDate;
15450 this.sortType = st.none;
15455 var stripRe = /[\$,%]/g;
15457 // prebuilt conversion function for this field, instead of
15458 // switching every time we're reading a value
15460 var cv, dateFormat = this.dateFormat;
15465 cv = function(v){ return v; };
15468 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15472 return v !== undefined && v !== null && v !== '' ?
15473 parseInt(String(v).replace(stripRe, ""), 10) : '';
15478 return v !== undefined && v !== null && v !== '' ?
15479 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15484 cv = function(v){ return v === true || v === "true" || v == 1; };
15491 if(v instanceof Date){
15495 if(dateFormat == "timestamp"){
15496 return new Date(v*1000);
15498 return Date.parseDate(v, dateFormat);
15500 var parsed = Date.parse(v);
15501 return parsed ? new Date(parsed) : null;
15510 Roo.data.Field.prototype = {
15518 * Ext JS Library 1.1.1
15519 * Copyright(c) 2006-2007, Ext JS, LLC.
15521 * Originally Released Under LGPL - original licence link has changed is not relivant.
15524 * <script type="text/javascript">
15527 // Base class for reading structured data from a data source. This class is intended to be
15528 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15531 * @class Roo.data.DataReader
15533 * Base class for reading structured data from a data source. This class is intended to be
15534 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15537 Roo.data.DataReader = function(meta, recordType){
15541 this.recordType = recordType instanceof Array ?
15542 Roo.data.Record.create(recordType) : recordType;
15545 Roo.data.DataReader.prototype = {
15548 readerType : 'Data',
15550 * Create an empty record
15551 * @param {Object} data (optional) - overlay some values
15552 * @return {Roo.data.Record} record created.
15554 newRow : function(d) {
15556 this.recordType.prototype.fields.each(function(c) {
15558 case 'int' : da[c.name] = 0; break;
15559 case 'date' : da[c.name] = new Date(); break;
15560 case 'float' : da[c.name] = 0.0; break;
15561 case 'boolean' : da[c.name] = false; break;
15562 default : da[c.name] = ""; break;
15566 return new this.recordType(Roo.apply(da, d));
15572 * Ext JS Library 1.1.1
15573 * Copyright(c) 2006-2007, Ext JS, LLC.
15575 * Originally Released Under LGPL - original licence link has changed is not relivant.
15578 * <script type="text/javascript">
15582 * @class Roo.data.DataProxy
15583 * @extends Roo.data.Observable
15585 * This class is an abstract base class for implementations which provide retrieval of
15586 * unformatted data objects.<br>
15588 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15589 * (of the appropriate type which knows how to parse the data object) to provide a block of
15590 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15592 * Custom implementations must implement the load method as described in
15593 * {@link Roo.data.HttpProxy#load}.
15595 Roo.data.DataProxy = function(){
15598 * @event beforeload
15599 * Fires before a network request is made to retrieve a data object.
15600 * @param {Object} This DataProxy object.
15601 * @param {Object} params The params parameter to the load function.
15606 * Fires before the load method's callback is called.
15607 * @param {Object} This DataProxy object.
15608 * @param {Object} o The data object.
15609 * @param {Object} arg The callback argument object passed to the load function.
15613 * @event loadexception
15614 * Fires if an Exception occurs during data retrieval.
15615 * @param {Object} This DataProxy object.
15616 * @param {Object} o The data object.
15617 * @param {Object} arg The callback argument object passed to the load function.
15618 * @param {Object} e The Exception.
15620 loadexception : true
15622 Roo.data.DataProxy.superclass.constructor.call(this);
15625 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15628 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15632 * Ext JS Library 1.1.1
15633 * Copyright(c) 2006-2007, Ext JS, LLC.
15635 * Originally Released Under LGPL - original licence link has changed is not relivant.
15638 * <script type="text/javascript">
15641 * @class Roo.data.MemoryProxy
15642 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15643 * to the Reader when its load method is called.
15645 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15647 Roo.data.MemoryProxy = function(data){
15651 Roo.data.MemoryProxy.superclass.constructor.call(this);
15655 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15658 * Load data from the requested source (in this case an in-memory
15659 * data object passed to the constructor), read the data object into
15660 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15661 * process that block using the passed callback.
15662 * @param {Object} params This parameter is not used by the MemoryProxy class.
15663 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15664 * object into a block of Roo.data.Records.
15665 * @param {Function} callback The function into which to pass the block of Roo.data.records.
15666 * The function must be passed <ul>
15667 * <li>The Record block object</li>
15668 * <li>The "arg" argument from the load function</li>
15669 * <li>A boolean success indicator</li>
15671 * @param {Object} scope The scope in which to call the callback
15672 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15674 load : function(params, reader, callback, scope, arg){
15675 params = params || {};
15678 result = reader.readRecords(params.data ? params.data :this.data);
15680 this.fireEvent("loadexception", this, arg, null, e);
15681 callback.call(scope, null, arg, false);
15684 callback.call(scope, result, arg, true);
15688 update : function(params, records){
15693 * Ext JS Library 1.1.1
15694 * Copyright(c) 2006-2007, Ext JS, LLC.
15696 * Originally Released Under LGPL - original licence link has changed is not relivant.
15699 * <script type="text/javascript">
15702 * @class Roo.data.HttpProxy
15703 * @extends Roo.data.DataProxy
15704 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15705 * configured to reference a certain URL.<br><br>
15707 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15708 * from which the running page was served.<br><br>
15710 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15712 * Be aware that to enable the browser to parse an XML document, the server must set
15713 * the Content-Type header in the HTTP response to "text/xml".
15715 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15716 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
15717 * will be used to make the request.
15719 Roo.data.HttpProxy = function(conn){
15720 Roo.data.HttpProxy.superclass.constructor.call(this);
15721 // is conn a conn config or a real conn?
15723 this.useAjax = !conn || !conn.events;
15727 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15728 // thse are take from connection...
15731 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15734 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15735 * extra parameters to each request made by this object. (defaults to undefined)
15738 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15739 * to each request made by this object. (defaults to undefined)
15742 * @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)
15745 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15748 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15754 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15758 * Return the {@link Roo.data.Connection} object being used by this Proxy.
15759 * @return {Connection} The Connection object. This object may be used to subscribe to events on
15760 * a finer-grained basis than the DataProxy events.
15762 getConnection : function(){
15763 return this.useAjax ? Roo.Ajax : this.conn;
15767 * Load data from the configured {@link Roo.data.Connection}, read the data object into
15768 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15769 * process that block using the passed callback.
15770 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15771 * for the request to the remote server.
15772 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15773 * object into a block of Roo.data.Records.
15774 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15775 * The function must be passed <ul>
15776 * <li>The Record block object</li>
15777 * <li>The "arg" argument from the load function</li>
15778 * <li>A boolean success indicator</li>
15780 * @param {Object} scope The scope in which to call the callback
15781 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15783 load : function(params, reader, callback, scope, arg){
15784 if(this.fireEvent("beforeload", this, params) !== false){
15786 params : params || {},
15788 callback : callback,
15793 callback : this.loadResponse,
15797 Roo.applyIf(o, this.conn);
15798 if(this.activeRequest){
15799 Roo.Ajax.abort(this.activeRequest);
15801 this.activeRequest = Roo.Ajax.request(o);
15803 this.conn.request(o);
15806 callback.call(scope||this, null, arg, false);
15811 loadResponse : function(o, success, response){
15812 delete this.activeRequest;
15814 this.fireEvent("loadexception", this, o, response);
15815 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15820 result = o.reader.read(response);
15822 this.fireEvent("loadexception", this, o, response, e);
15823 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15827 this.fireEvent("load", this, o, o.request.arg);
15828 o.request.callback.call(o.request.scope, result, o.request.arg, true);
15832 update : function(dataSet){
15837 updateResponse : function(dataSet){
15842 * Ext JS Library 1.1.1
15843 * Copyright(c) 2006-2007, Ext JS, LLC.
15845 * Originally Released Under LGPL - original licence link has changed is not relivant.
15848 * <script type="text/javascript">
15852 * @class Roo.data.ScriptTagProxy
15853 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15854 * other than the originating domain of the running page.<br><br>
15856 * <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
15857 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15859 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15860 * source code that is used as the source inside a <script> tag.<br><br>
15862 * In order for the browser to process the returned data, the server must wrap the data object
15863 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15864 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15865 * depending on whether the callback name was passed:
15868 boolean scriptTag = false;
15869 String cb = request.getParameter("callback");
15872 response.setContentType("text/javascript");
15874 response.setContentType("application/x-json");
15876 Writer out = response.getWriter();
15878 out.write(cb + "(");
15880 out.print(dataBlock.toJsonString());
15887 * @param {Object} config A configuration object.
15889 Roo.data.ScriptTagProxy = function(config){
15890 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15891 Roo.apply(this, config);
15892 this.head = document.getElementsByTagName("head")[0];
15895 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15897 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15899 * @cfg {String} url The URL from which to request the data object.
15902 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15906 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15907 * the server the name of the callback function set up by the load call to process the returned data object.
15908 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15909 * javascript output which calls this named function passing the data object as its only parameter.
15911 callbackParam : "callback",
15913 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15914 * name to the request.
15919 * Load data from the configured URL, read the data object into
15920 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15921 * process that block using the passed callback.
15922 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15923 * for the request to the remote server.
15924 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15925 * object into a block of Roo.data.Records.
15926 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15927 * The function must be passed <ul>
15928 * <li>The Record block object</li>
15929 * <li>The "arg" argument from the load function</li>
15930 * <li>A boolean success indicator</li>
15932 * @param {Object} scope The scope in which to call the callback
15933 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15935 load : function(params, reader, callback, scope, arg){
15936 if(this.fireEvent("beforeload", this, params) !== false){
15938 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15940 var url = this.url;
15941 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15943 url += "&_dc=" + (new Date().getTime());
15945 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15948 cb : "stcCallback"+transId,
15949 scriptId : "stcScript"+transId,
15953 callback : callback,
15959 window[trans.cb] = function(o){
15960 conn.handleResponse(o, trans);
15963 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15965 if(this.autoAbort !== false){
15969 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15971 var script = document.createElement("script");
15972 script.setAttribute("src", url);
15973 script.setAttribute("type", "text/javascript");
15974 script.setAttribute("id", trans.scriptId);
15975 this.head.appendChild(script);
15977 this.trans = trans;
15979 callback.call(scope||this, null, arg, false);
15984 isLoading : function(){
15985 return this.trans ? true : false;
15989 * Abort the current server request.
15991 abort : function(){
15992 if(this.isLoading()){
15993 this.destroyTrans(this.trans);
15998 destroyTrans : function(trans, isLoaded){
15999 this.head.removeChild(document.getElementById(trans.scriptId));
16000 clearTimeout(trans.timeoutId);
16002 window[trans.cb] = undefined;
16004 delete window[trans.cb];
16007 // if hasn't been loaded, wait for load to remove it to prevent script error
16008 window[trans.cb] = function(){
16009 window[trans.cb] = undefined;
16011 delete window[trans.cb];
16018 handleResponse : function(o, trans){
16019 this.trans = false;
16020 this.destroyTrans(trans, true);
16023 result = trans.reader.readRecords(o);
16025 this.fireEvent("loadexception", this, o, trans.arg, e);
16026 trans.callback.call(trans.scope||window, null, trans.arg, false);
16029 this.fireEvent("load", this, o, trans.arg);
16030 trans.callback.call(trans.scope||window, result, trans.arg, true);
16034 handleFailure : function(trans){
16035 this.trans = false;
16036 this.destroyTrans(trans, false);
16037 this.fireEvent("loadexception", this, null, trans.arg);
16038 trans.callback.call(trans.scope||window, null, trans.arg, false);
16042 * Ext JS Library 1.1.1
16043 * Copyright(c) 2006-2007, Ext JS, LLC.
16045 * Originally Released Under LGPL - original licence link has changed is not relivant.
16048 * <script type="text/javascript">
16052 * @class Roo.data.JsonReader
16053 * @extends Roo.data.DataReader
16054 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16055 * based on mappings in a provided Roo.data.Record constructor.
16057 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16058 * in the reply previously.
16063 var RecordDef = Roo.data.Record.create([
16064 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16065 {name: 'occupation'} // This field will use "occupation" as the mapping.
16067 var myReader = new Roo.data.JsonReader({
16068 totalProperty: "results", // The property which contains the total dataset size (optional)
16069 root: "rows", // The property which contains an Array of row objects
16070 id: "id" // The property within each row object that provides an ID for the record (optional)
16074 * This would consume a JSON file like this:
16076 { 'results': 2, 'rows': [
16077 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16078 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16081 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16082 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16083 * paged from the remote server.
16084 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16085 * @cfg {String} root name of the property which contains the Array of row objects.
16086 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16087 * @cfg {Array} fields Array of field definition objects
16089 * Create a new JsonReader
16090 * @param {Object} meta Metadata configuration options
16091 * @param {Object} recordType Either an Array of field definition objects,
16092 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16094 Roo.data.JsonReader = function(meta, recordType){
16097 // set some defaults:
16098 Roo.applyIf(meta, {
16099 totalProperty: 'total',
16100 successProperty : 'success',
16105 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16107 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16109 readerType : 'Json',
16112 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16113 * Used by Store query builder to append _requestMeta to params.
16116 metaFromRemote : false,
16118 * This method is only used by a DataProxy which has retrieved data from a remote server.
16119 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16120 * @return {Object} data A data block which is used by an Roo.data.Store object as
16121 * a cache of Roo.data.Records.
16123 read : function(response){
16124 var json = response.responseText;
16126 var o = /* eval:var:o */ eval("("+json+")");
16128 throw {message: "JsonReader.read: Json object not found"};
16134 this.metaFromRemote = true;
16135 this.meta = o.metaData;
16136 this.recordType = Roo.data.Record.create(o.metaData.fields);
16137 this.onMetaChange(this.meta, this.recordType, o);
16139 return this.readRecords(o);
16142 // private function a store will implement
16143 onMetaChange : function(meta, recordType, o){
16150 simpleAccess: function(obj, subsc) {
16157 getJsonAccessor: function(){
16159 return function(expr) {
16161 return(re.test(expr))
16162 ? new Function("obj", "return obj." + expr)
16167 return Roo.emptyFn;
16172 * Create a data block containing Roo.data.Records from an XML document.
16173 * @param {Object} o An object which contains an Array of row objects in the property specified
16174 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16175 * which contains the total size of the dataset.
16176 * @return {Object} data A data block which is used by an Roo.data.Store object as
16177 * a cache of Roo.data.Records.
16179 readRecords : function(o){
16181 * After any data loads, the raw JSON data is available for further custom processing.
16185 var s = this.meta, Record = this.recordType,
16186 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16188 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16190 if(s.totalProperty) {
16191 this.getTotal = this.getJsonAccessor(s.totalProperty);
16193 if(s.successProperty) {
16194 this.getSuccess = this.getJsonAccessor(s.successProperty);
16196 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16198 var g = this.getJsonAccessor(s.id);
16199 this.getId = function(rec) {
16201 return (r === undefined || r === "") ? null : r;
16204 this.getId = function(){return null;};
16207 for(var jj = 0; jj < fl; jj++){
16209 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16210 this.ef[jj] = this.getJsonAccessor(map);
16214 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16215 if(s.totalProperty){
16216 var vt = parseInt(this.getTotal(o), 10);
16221 if(s.successProperty){
16222 var vs = this.getSuccess(o);
16223 if(vs === false || vs === 'false'){
16228 for(var i = 0; i < c; i++){
16231 var id = this.getId(n);
16232 for(var j = 0; j < fl; j++){
16234 var v = this.ef[j](n);
16236 Roo.log('missing convert for ' + f.name);
16240 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16242 var record = new Record(values, id);
16244 records[i] = record;
16250 totalRecords : totalRecords
16253 // used when loading children.. @see loadDataFromChildren
16254 toLoadData: function(rec)
16256 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16257 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16258 return { data : data, total : data.length };
16263 * Ext JS Library 1.1.1
16264 * Copyright(c) 2006-2007, Ext JS, LLC.
16266 * Originally Released Under LGPL - original licence link has changed is not relivant.
16269 * <script type="text/javascript">
16273 * @class Roo.data.ArrayReader
16274 * @extends Roo.data.DataReader
16275 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16276 * Each element of that Array represents a row of data fields. The
16277 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16278 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16282 var RecordDef = Roo.data.Record.create([
16283 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16284 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16286 var myReader = new Roo.data.ArrayReader({
16287 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16291 * This would consume an Array like this:
16293 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16297 * Create a new JsonReader
16298 * @param {Object} meta Metadata configuration options.
16299 * @param {Object|Array} recordType Either an Array of field definition objects
16301 * @cfg {Array} fields Array of field definition objects
16302 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16303 * as specified to {@link Roo.data.Record#create},
16304 * or an {@link Roo.data.Record} object
16307 * created using {@link Roo.data.Record#create}.
16309 Roo.data.ArrayReader = function(meta, recordType)
16311 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16314 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16317 * Create a data block containing Roo.data.Records from an XML document.
16318 * @param {Object} o An Array of row objects which represents the dataset.
16319 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16320 * a cache of Roo.data.Records.
16322 readRecords : function(o)
16324 var sid = this.meta ? this.meta.id : null;
16325 var recordType = this.recordType, fields = recordType.prototype.fields;
16328 for(var i = 0; i < root.length; i++){
16331 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16332 for(var j = 0, jlen = fields.length; j < jlen; j++){
16333 var f = fields.items[j];
16334 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16335 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16337 values[f.name] = v;
16339 var record = new recordType(values, id);
16341 records[records.length] = record;
16345 totalRecords : records.length
16348 // used when loading children.. @see loadDataFromChildren
16349 toLoadData: function(rec)
16351 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16352 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16363 * @class Roo.bootstrap.ComboBox
16364 * @extends Roo.bootstrap.TriggerField
16365 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16366 * @cfg {Boolean} append (true|false) default false
16367 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16368 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16369 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16370 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16371 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16372 * @cfg {Boolean} animate default true
16373 * @cfg {Boolean} emptyResultText only for touch device
16374 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16375 * @cfg {String} emptyTitle default ''
16376 * @cfg {Number} width fixed with? experimental
16378 * Create a new ComboBox.
16379 * @param {Object} config Configuration options
16381 Roo.bootstrap.ComboBox = function(config){
16382 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16386 * Fires when the dropdown list is expanded
16387 * @param {Roo.bootstrap.ComboBox} combo This combo box
16392 * Fires when the dropdown list is collapsed
16393 * @param {Roo.bootstrap.ComboBox} combo This combo box
16397 * @event beforeselect
16398 * Fires before a list item is selected. Return false to cancel the selection.
16399 * @param {Roo.bootstrap.ComboBox} combo This combo box
16400 * @param {Roo.data.Record} record The data record returned from the underlying store
16401 * @param {Number} index The index of the selected item in the dropdown list
16403 'beforeselect' : true,
16406 * Fires when a list item is selected
16407 * @param {Roo.bootstrap.ComboBox} combo This combo box
16408 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16409 * @param {Number} index The index of the selected item in the dropdown list
16413 * @event beforequery
16414 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16415 * The event object passed has these properties:
16416 * @param {Roo.bootstrap.ComboBox} combo This combo box
16417 * @param {String} query The query
16418 * @param {Boolean} forceAll true to force "all" query
16419 * @param {Boolean} cancel true to cancel the query
16420 * @param {Object} e The query event object
16422 'beforequery': true,
16425 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16426 * @param {Roo.bootstrap.ComboBox} combo This combo box
16431 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16432 * @param {Roo.bootstrap.ComboBox} combo This combo box
16433 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16438 * Fires when the remove value from the combobox array
16439 * @param {Roo.bootstrap.ComboBox} combo This combo box
16443 * @event afterremove
16444 * Fires when the remove value from the combobox array
16445 * @param {Roo.bootstrap.ComboBox} combo This combo box
16447 'afterremove' : true,
16449 * @event specialfilter
16450 * Fires when specialfilter
16451 * @param {Roo.bootstrap.ComboBox} combo This combo box
16453 'specialfilter' : true,
16456 * Fires when tick the element
16457 * @param {Roo.bootstrap.ComboBox} combo This combo box
16461 * @event touchviewdisplay
16462 * Fires when touch view require special display (default is using displayField)
16463 * @param {Roo.bootstrap.ComboBox} combo This combo box
16464 * @param {Object} cfg set html .
16466 'touchviewdisplay' : true
16471 this.tickItems = [];
16473 this.selectedIndex = -1;
16474 if(this.mode == 'local'){
16475 if(config.queryDelay === undefined){
16476 this.queryDelay = 10;
16478 if(config.minChars === undefined){
16484 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16487 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16488 * rendering into an Roo.Editor, defaults to false)
16491 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16492 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16495 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16498 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16499 * the dropdown list (defaults to undefined, with no header element)
16503 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16507 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16509 listWidth: undefined,
16511 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16512 * mode = 'remote' or 'text' if mode = 'local')
16514 displayField: undefined,
16517 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16518 * mode = 'remote' or 'value' if mode = 'local').
16519 * Note: use of a valueField requires the user make a selection
16520 * in order for a value to be mapped.
16522 valueField: undefined,
16524 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16529 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16530 * field's data value (defaults to the underlying DOM element's name)
16532 hiddenName: undefined,
16534 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16538 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16540 selectedClass: 'active',
16543 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16547 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16548 * anchor positions (defaults to 'tl-bl')
16550 listAlign: 'tl-bl?',
16552 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16556 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16557 * query specified by the allQuery config option (defaults to 'query')
16559 triggerAction: 'query',
16561 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16562 * (defaults to 4, does not apply if editable = false)
16566 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16567 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16571 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16572 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16576 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16577 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16581 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16582 * when editable = true (defaults to false)
16584 selectOnFocus:false,
16586 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16588 queryParam: 'query',
16590 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
16591 * when mode = 'remote' (defaults to 'Loading...')
16593 loadingText: 'Loading...',
16595 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16599 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16603 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16604 * traditional select (defaults to true)
16608 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16612 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16616 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16617 * listWidth has a higher value)
16621 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16622 * allow the user to set arbitrary text into the field (defaults to false)
16624 forceSelection:false,
16626 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16627 * if typeAhead = true (defaults to 250)
16629 typeAheadDelay : 250,
16631 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16632 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16634 valueNotFoundText : undefined,
16636 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16638 blockFocus : false,
16641 * @cfg {Boolean} disableClear Disable showing of clear button.
16643 disableClear : false,
16645 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
16647 alwaysQuery : false,
16650 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
16655 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16657 invalidClass : "has-warning",
16660 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16662 validClass : "has-success",
16665 * @cfg {Boolean} specialFilter (true|false) special filter default false
16667 specialFilter : false,
16670 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16672 mobileTouchView : true,
16675 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16677 useNativeIOS : false,
16680 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16682 mobile_restrict_height : false,
16684 ios_options : false,
16696 btnPosition : 'right',
16697 triggerList : true,
16698 showToggleBtn : true,
16700 emptyResultText: 'Empty',
16701 triggerText : 'Select',
16705 // element that contains real text value.. (when hidden is used..)
16707 getAutoCreate : function()
16712 * Render classic select for iso
16715 if(Roo.isIOS && this.useNativeIOS){
16716 cfg = this.getAutoCreateNativeIOS();
16724 if(Roo.isTouch && this.mobileTouchView){
16725 cfg = this.getAutoCreateTouchView();
16732 if(!this.tickable){
16733 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16738 * ComboBox with tickable selections
16741 var align = this.labelAlign || this.parentLabelAlign();
16744 cls : 'form-group roo-combobox-tickable' //input-group
16747 var btn_text_select = '';
16748 var btn_text_done = '';
16749 var btn_text_cancel = '';
16751 if (this.btn_text_show) {
16752 btn_text_select = 'Select';
16753 btn_text_done = 'Done';
16754 btn_text_cancel = 'Cancel';
16759 cls : 'tickable-buttons',
16764 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16765 //html : this.triggerText
16766 html: btn_text_select
16772 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16774 html: btn_text_done
16780 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16782 html: btn_text_cancel
16788 buttons.cn.unshift({
16790 cls: 'roo-select2-search-field-input'
16796 Roo.each(buttons.cn, function(c){
16798 c.cls += ' btn-' + _this.size;
16801 if (_this.disabled) {
16808 style : 'display: contents',
16813 cls: 'form-hidden-field'
16817 cls: 'roo-select2-choices',
16821 cls: 'roo-select2-search-field',
16832 cls: 'roo-select2-container input-group roo-select2-container-multi',
16838 // cls: 'typeahead typeahead-long dropdown-menu',
16839 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
16844 if(this.hasFeedback && !this.allowBlank){
16848 cls: 'glyphicon form-control-feedback'
16851 combobox.cn.push(feedback);
16858 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16859 tooltip : 'This field is required'
16861 if (Roo.bootstrap.version == 4) {
16864 style : 'display:none'
16867 if (align ==='left' && this.fieldLabel.length) {
16869 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
16876 cls : 'control-label col-form-label',
16877 html : this.fieldLabel
16889 var labelCfg = cfg.cn[1];
16890 var contentCfg = cfg.cn[2];
16893 if(this.indicatorpos == 'right'){
16899 cls : 'control-label col-form-label',
16903 html : this.fieldLabel
16919 labelCfg = cfg.cn[0];
16920 contentCfg = cfg.cn[1];
16924 if(this.labelWidth > 12){
16925 labelCfg.style = "width: " + this.labelWidth + 'px';
16927 if(this.width * 1 > 0){
16928 contentCfg.style = "width: " + this.width + 'px';
16930 if(this.labelWidth < 13 && this.labelmd == 0){
16931 this.labelmd = this.labelWidth;
16934 if(this.labellg > 0){
16935 labelCfg.cls += ' col-lg-' + this.labellg;
16936 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16939 if(this.labelmd > 0){
16940 labelCfg.cls += ' col-md-' + this.labelmd;
16941 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16944 if(this.labelsm > 0){
16945 labelCfg.cls += ' col-sm-' + this.labelsm;
16946 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16949 if(this.labelxs > 0){
16950 labelCfg.cls += ' col-xs-' + this.labelxs;
16951 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16955 } else if ( this.fieldLabel.length) {
16956 // Roo.log(" label");
16961 //cls : 'input-group-addon',
16962 html : this.fieldLabel
16967 if(this.indicatorpos == 'right'){
16971 //cls : 'input-group-addon',
16972 html : this.fieldLabel
16982 // Roo.log(" no label && no align");
16989 ['xs','sm','md','lg'].map(function(size){
16990 if (settings[size]) {
16991 cfg.cls += ' col-' + size + '-' + settings[size];
16999 _initEventsCalled : false,
17002 initEvents: function()
17004 if (this._initEventsCalled) { // as we call render... prevent looping...
17007 this._initEventsCalled = true;
17010 throw "can not find store for combo";
17013 this.indicator = this.indicatorEl();
17015 this.store = Roo.factory(this.store, Roo.data);
17016 this.store.parent = this;
17018 // if we are building from html. then this element is so complex, that we can not really
17019 // use the rendered HTML.
17020 // so we have to trash and replace the previous code.
17021 if (Roo.XComponent.build_from_html) {
17022 // remove this element....
17023 var e = this.el.dom, k=0;
17024 while (e ) { e = e.previousSibling; ++k;}
17029 this.rendered = false;
17031 this.render(this.parent().getChildContainer(true), k);
17034 if(Roo.isIOS && this.useNativeIOS){
17035 this.initIOSView();
17043 if(Roo.isTouch && this.mobileTouchView){
17044 this.initTouchView();
17049 this.initTickableEvents();
17053 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17055 if(this.hiddenName){
17057 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17059 this.hiddenField.dom.value =
17060 this.hiddenValue !== undefined ? this.hiddenValue :
17061 this.value !== undefined ? this.value : '';
17063 // prevent input submission
17064 this.el.dom.removeAttribute('name');
17065 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17070 // this.el.dom.setAttribute('autocomplete', 'off');
17073 var cls = 'x-combo-list';
17075 //this.list = new Roo.Layer({
17076 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17082 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17083 _this.list.setWidth(lw);
17086 this.list.on('mouseover', this.onViewOver, this);
17087 this.list.on('mousemove', this.onViewMove, this);
17088 this.list.on('scroll', this.onViewScroll, this);
17091 this.list.swallowEvent('mousewheel');
17092 this.assetHeight = 0;
17095 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17096 this.assetHeight += this.header.getHeight();
17099 this.innerList = this.list.createChild({cls:cls+'-inner'});
17100 this.innerList.on('mouseover', this.onViewOver, this);
17101 this.innerList.on('mousemove', this.onViewMove, this);
17102 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17104 if(this.allowBlank && !this.pageSize && !this.disableClear){
17105 this.footer = this.list.createChild({cls:cls+'-ft'});
17106 this.pageTb = new Roo.Toolbar(this.footer);
17110 this.footer = this.list.createChild({cls:cls+'-ft'});
17111 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17112 {pageSize: this.pageSize});
17116 if (this.pageTb && this.allowBlank && !this.disableClear) {
17118 this.pageTb.add(new Roo.Toolbar.Fill(), {
17119 cls: 'x-btn-icon x-btn-clear',
17121 handler: function()
17124 _this.clearValue();
17125 _this.onSelect(false, -1);
17130 this.assetHeight += this.footer.getHeight();
17135 this.tpl = Roo.bootstrap.version == 4 ?
17136 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17137 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17140 this.view = new Roo.View(this.list, this.tpl, {
17141 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17143 //this.view.wrapEl.setDisplayed(false);
17144 this.view.on('click', this.onViewClick, this);
17147 this.store.on('beforeload', this.onBeforeLoad, this);
17148 this.store.on('load', this.onLoad, this);
17149 this.store.on('loadexception', this.onLoadException, this);
17151 if(this.resizable){
17152 this.resizer = new Roo.Resizable(this.list, {
17153 pinned:true, handles:'se'
17155 this.resizer.on('resize', function(r, w, h){
17156 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17157 this.listWidth = w;
17158 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17159 this.restrictHeight();
17161 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17164 if(!this.editable){
17165 this.editable = true;
17166 this.setEditable(false);
17171 if (typeof(this.events.add.listeners) != 'undefined') {
17173 this.addicon = this.wrap.createChild(
17174 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17176 this.addicon.on('click', function(e) {
17177 this.fireEvent('add', this);
17180 if (typeof(this.events.edit.listeners) != 'undefined') {
17182 this.editicon = this.wrap.createChild(
17183 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17184 if (this.addicon) {
17185 this.editicon.setStyle('margin-left', '40px');
17187 this.editicon.on('click', function(e) {
17189 // we fire even if inothing is selected..
17190 this.fireEvent('edit', this, this.lastData );
17196 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17197 "up" : function(e){
17198 this.inKeyMode = true;
17202 "down" : function(e){
17203 if(!this.isExpanded()){
17204 this.onTriggerClick();
17206 this.inKeyMode = true;
17211 "enter" : function(e){
17212 // this.onViewClick();
17216 if(this.fireEvent("specialkey", this, e)){
17217 this.onViewClick(false);
17223 "esc" : function(e){
17227 "tab" : function(e){
17230 if(this.fireEvent("specialkey", this, e)){
17231 this.onViewClick(false);
17239 doRelay : function(foo, bar, hname){
17240 if(hname == 'down' || this.scope.isExpanded()){
17241 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17250 this.queryDelay = Math.max(this.queryDelay || 10,
17251 this.mode == 'local' ? 10 : 250);
17254 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17256 if(this.typeAhead){
17257 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17259 if(this.editable !== false){
17260 this.inputEl().on("keyup", this.onKeyUp, this);
17262 if(this.forceSelection){
17263 this.inputEl().on('blur', this.doForce, this);
17267 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17268 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17272 initTickableEvents: function()
17276 if(this.hiddenName){
17278 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17280 this.hiddenField.dom.value =
17281 this.hiddenValue !== undefined ? this.hiddenValue :
17282 this.value !== undefined ? this.value : '';
17284 // prevent input submission
17285 this.el.dom.removeAttribute('name');
17286 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17291 // this.list = this.el.select('ul.dropdown-menu',true).first();
17293 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17294 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17295 if(this.triggerList){
17296 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17299 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17300 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17302 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17303 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17305 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17306 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17308 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17309 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17310 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17313 this.cancelBtn.hide();
17318 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17319 _this.list.setWidth(lw);
17322 this.list.on('mouseover', this.onViewOver, this);
17323 this.list.on('mousemove', this.onViewMove, this);
17325 this.list.on('scroll', this.onViewScroll, this);
17328 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17329 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17332 this.view = new Roo.View(this.list, this.tpl, {
17337 selectedClass: this.selectedClass
17340 //this.view.wrapEl.setDisplayed(false);
17341 this.view.on('click', this.onViewClick, this);
17345 this.store.on('beforeload', this.onBeforeLoad, this);
17346 this.store.on('load', this.onLoad, this);
17347 this.store.on('loadexception', this.onLoadException, this);
17350 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17351 "up" : function(e){
17352 this.inKeyMode = true;
17356 "down" : function(e){
17357 this.inKeyMode = true;
17361 "enter" : function(e){
17362 if(this.fireEvent("specialkey", this, e)){
17363 this.onViewClick(false);
17369 "esc" : function(e){
17370 this.onTickableFooterButtonClick(e, false, false);
17373 "tab" : function(e){
17374 this.fireEvent("specialkey", this, e);
17376 this.onTickableFooterButtonClick(e, false, false);
17383 doRelay : function(e, fn, key){
17384 if(this.scope.isExpanded()){
17385 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17394 this.queryDelay = Math.max(this.queryDelay || 10,
17395 this.mode == 'local' ? 10 : 250);
17398 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17400 if(this.typeAhead){
17401 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17404 if(this.editable !== false){
17405 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17408 this.indicator = this.indicatorEl();
17410 if(this.indicator){
17411 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17412 this.indicator.hide();
17417 onDestroy : function(){
17419 this.view.setStore(null);
17420 this.view.el.removeAllListeners();
17421 this.view.el.remove();
17422 this.view.purgeListeners();
17425 this.list.dom.innerHTML = '';
17429 this.store.un('beforeload', this.onBeforeLoad, this);
17430 this.store.un('load', this.onLoad, this);
17431 this.store.un('loadexception', this.onLoadException, this);
17433 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17437 fireKey : function(e){
17438 if(e.isNavKeyPress() && !this.list.isVisible()){
17439 this.fireEvent("specialkey", this, e);
17444 onResize: function(w, h)
17448 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17450 // if(typeof w != 'number'){
17451 // // we do not handle it!?!?
17454 // var tw = this.trigger.getWidth();
17455 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17456 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17458 // this.inputEl().setWidth( this.adjustWidth('input', x));
17460 // //this.trigger.setStyle('left', x+'px');
17462 // if(this.list && this.listWidth === undefined){
17463 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17464 // this.list.setWidth(lw);
17465 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17473 * Allow or prevent the user from directly editing the field text. If false is passed,
17474 * the user will only be able to select from the items defined in the dropdown list. This method
17475 * is the runtime equivalent of setting the 'editable' config option at config time.
17476 * @param {Boolean} value True to allow the user to directly edit the field text
17478 setEditable : function(value){
17479 if(value == this.editable){
17482 this.editable = value;
17484 this.inputEl().dom.setAttribute('readOnly', true);
17485 this.inputEl().on('mousedown', this.onTriggerClick, this);
17486 this.inputEl().addClass('x-combo-noedit');
17488 this.inputEl().dom.removeAttribute('readOnly');
17489 this.inputEl().un('mousedown', this.onTriggerClick, this);
17490 this.inputEl().removeClass('x-combo-noedit');
17496 onBeforeLoad : function(combo,opts){
17497 if(!this.hasFocus){
17501 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17503 this.restrictHeight();
17504 this.selectedIndex = -1;
17508 onLoad : function(){
17510 this.hasQuery = false;
17512 if(!this.hasFocus){
17516 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17517 this.loading.hide();
17520 if(this.store.getCount() > 0){
17523 this.restrictHeight();
17524 if(this.lastQuery == this.allQuery){
17525 if(this.editable && !this.tickable){
17526 this.inputEl().dom.select();
17530 !this.selectByValue(this.value, true) &&
17533 !this.store.lastOptions ||
17534 typeof(this.store.lastOptions.add) == 'undefined' ||
17535 this.store.lastOptions.add != true
17538 this.select(0, true);
17541 if(this.autoFocus){
17544 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17545 this.taTask.delay(this.typeAheadDelay);
17549 this.onEmptyResults();
17555 onLoadException : function()
17557 this.hasQuery = false;
17559 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17560 this.loading.hide();
17563 if(this.tickable && this.editable){
17568 // only causes errors at present
17569 //Roo.log(this.store.reader.jsonData);
17570 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17572 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17578 onTypeAhead : function(){
17579 if(this.store.getCount() > 0){
17580 var r = this.store.getAt(0);
17581 var newValue = r.data[this.displayField];
17582 var len = newValue.length;
17583 var selStart = this.getRawValue().length;
17585 if(selStart != len){
17586 this.setRawValue(newValue);
17587 this.selectText(selStart, newValue.length);
17593 onSelect : function(record, index){
17595 if(this.fireEvent('beforeselect', this, record, index) !== false){
17597 this.setFromData(index > -1 ? record.data : false);
17600 this.fireEvent('select', this, record, index);
17605 * Returns the currently selected field value or empty string if no value is set.
17606 * @return {String} value The selected value
17608 getValue : function()
17610 if(Roo.isIOS && this.useNativeIOS){
17611 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17615 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17618 if(this.valueField){
17619 return typeof this.value != 'undefined' ? this.value : '';
17621 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17625 getRawValue : function()
17627 if(Roo.isIOS && this.useNativeIOS){
17628 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17631 var v = this.inputEl().getValue();
17637 * Clears any text/value currently set in the field
17639 clearValue : function(){
17641 if(this.hiddenField){
17642 this.hiddenField.dom.value = '';
17645 this.setRawValue('');
17646 this.lastSelectionText = '';
17647 this.lastData = false;
17649 var close = this.closeTriggerEl();
17660 * Sets the specified value into the field. If the value finds a match, the corresponding record text
17661 * will be displayed in the field. If the value does not match the data value of an existing item,
17662 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17663 * Otherwise the field will be blank (although the value will still be set).
17664 * @param {String} value The value to match
17666 setValue : function(v)
17668 if(Roo.isIOS && this.useNativeIOS){
17669 this.setIOSValue(v);
17679 if(this.valueField){
17680 var r = this.findRecord(this.valueField, v);
17682 text = r.data[this.displayField];
17683 }else if(this.valueNotFoundText !== undefined){
17684 text = this.valueNotFoundText;
17687 this.lastSelectionText = text;
17688 if(this.hiddenField){
17689 this.hiddenField.dom.value = v;
17691 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17694 var close = this.closeTriggerEl();
17697 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17703 * @property {Object} the last set data for the element
17708 * Sets the value of the field based on a object which is related to the record format for the store.
17709 * @param {Object} value the value to set as. or false on reset?
17711 setFromData : function(o){
17718 var dv = ''; // display value
17719 var vv = ''; // value value..
17721 if (this.displayField) {
17722 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17724 // this is an error condition!!!
17725 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17728 if(this.valueField){
17729 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17732 var close = this.closeTriggerEl();
17735 if(dv.length || vv * 1 > 0){
17737 this.blockFocus=true;
17743 if(this.hiddenField){
17744 this.hiddenField.dom.value = vv;
17746 this.lastSelectionText = dv;
17747 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17751 // no hidden field.. - we store the value in 'value', but still display
17752 // display field!!!!
17753 this.lastSelectionText = dv;
17754 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17761 reset : function(){
17762 // overridden so that last data is reset..
17769 this.setValue(this.originalValue);
17770 //this.clearInvalid();
17771 this.lastData = false;
17773 this.view.clearSelections();
17779 findRecord : function(prop, value){
17781 if(this.store.getCount() > 0){
17782 this.store.each(function(r){
17783 if(r.data[prop] == value){
17793 getName: function()
17795 // returns hidden if it's set..
17796 if (!this.rendered) {return ''};
17797 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
17801 onViewMove : function(e, t){
17802 this.inKeyMode = false;
17806 onViewOver : function(e, t){
17807 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17810 var item = this.view.findItemFromChild(t);
17813 var index = this.view.indexOf(item);
17814 this.select(index, false);
17819 onViewClick : function(view, doFocus, el, e)
17821 var index = this.view.getSelectedIndexes()[0];
17823 var r = this.store.getAt(index);
17827 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17834 Roo.each(this.tickItems, function(v,k){
17836 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17838 _this.tickItems.splice(k, 1);
17840 if(typeof(e) == 'undefined' && view == false){
17841 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17853 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17854 this.tickItems.push(r.data);
17857 if(typeof(e) == 'undefined' && view == false){
17858 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17865 this.onSelect(r, index);
17867 if(doFocus !== false && !this.blockFocus){
17868 this.inputEl().focus();
17873 restrictHeight : function(){
17874 //this.innerList.dom.style.height = '';
17875 //var inner = this.innerList.dom;
17876 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17877 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17878 //this.list.beginUpdate();
17879 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17880 this.list.alignTo(this.inputEl(), this.listAlign);
17881 this.list.alignTo(this.inputEl(), this.listAlign);
17882 //this.list.endUpdate();
17886 onEmptyResults : function(){
17888 if(this.tickable && this.editable){
17889 this.hasFocus = false;
17890 this.restrictHeight();
17898 * Returns true if the dropdown list is expanded, else false.
17900 isExpanded : function(){
17901 return this.list.isVisible();
17905 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17906 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17907 * @param {String} value The data value of the item to select
17908 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17909 * selected item if it is not currently in view (defaults to true)
17910 * @return {Boolean} True if the value matched an item in the list, else false
17912 selectByValue : function(v, scrollIntoView){
17913 if(v !== undefined && v !== null){
17914 var r = this.findRecord(this.valueField || this.displayField, v);
17916 this.select(this.store.indexOf(r), scrollIntoView);
17924 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17925 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17926 * @param {Number} index The zero-based index of the list item to select
17927 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17928 * selected item if it is not currently in view (defaults to true)
17930 select : function(index, scrollIntoView){
17931 this.selectedIndex = index;
17932 this.view.select(index);
17933 if(scrollIntoView !== false){
17934 var el = this.view.getNode(index);
17936 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17939 this.list.scrollChildIntoView(el, false);
17945 selectNext : function(){
17946 var ct = this.store.getCount();
17948 if(this.selectedIndex == -1){
17950 }else if(this.selectedIndex < ct-1){
17951 this.select(this.selectedIndex+1);
17957 selectPrev : function(){
17958 var ct = this.store.getCount();
17960 if(this.selectedIndex == -1){
17962 }else if(this.selectedIndex != 0){
17963 this.select(this.selectedIndex-1);
17969 onKeyUp : function(e){
17970 if(this.editable !== false && !e.isSpecialKey()){
17971 this.lastKey = e.getKey();
17972 this.dqTask.delay(this.queryDelay);
17977 validateBlur : function(){
17978 return !this.list || !this.list.isVisible();
17982 initQuery : function(){
17984 var v = this.getRawValue();
17986 if(this.tickable && this.editable){
17987 v = this.tickableInputEl().getValue();
17994 doForce : function(){
17995 if(this.inputEl().dom.value.length > 0){
17996 this.inputEl().dom.value =
17997 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18003 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
18004 * query allowing the query action to be canceled if needed.
18005 * @param {String} query The SQL query to execute
18006 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18007 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18008 * saved in the current store (defaults to false)
18010 doQuery : function(q, forceAll){
18012 if(q === undefined || q === null){
18017 forceAll: forceAll,
18021 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18026 forceAll = qe.forceAll;
18027 if(forceAll === true || (q.length >= this.minChars)){
18029 this.hasQuery = true;
18031 if(this.lastQuery != q || this.alwaysQuery){
18032 this.lastQuery = q;
18033 if(this.mode == 'local'){
18034 this.selectedIndex = -1;
18036 this.store.clearFilter();
18039 if(this.specialFilter){
18040 this.fireEvent('specialfilter', this);
18045 this.store.filter(this.displayField, q);
18048 this.store.fireEvent("datachanged", this.store);
18055 this.store.baseParams[this.queryParam] = q;
18057 var options = {params : this.getParams(q)};
18060 options.add = true;
18061 options.params.start = this.page * this.pageSize;
18064 this.store.load(options);
18067 * this code will make the page width larger, at the beginning, the list not align correctly,
18068 * we should expand the list on onLoad
18069 * so command out it
18074 this.selectedIndex = -1;
18079 this.loadNext = false;
18083 getParams : function(q){
18085 //p[this.queryParam] = q;
18089 p.limit = this.pageSize;
18095 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18097 collapse : function(){
18098 if(!this.isExpanded()){
18104 this.hasFocus = false;
18108 this.cancelBtn.hide();
18109 this.trigger.show();
18112 this.tickableInputEl().dom.value = '';
18113 this.tickableInputEl().blur();
18118 Roo.get(document).un('mousedown', this.collapseIf, this);
18119 Roo.get(document).un('mousewheel', this.collapseIf, this);
18120 if (!this.editable) {
18121 Roo.get(document).un('keydown', this.listKeyPress, this);
18123 this.fireEvent('collapse', this);
18129 collapseIf : function(e){
18130 var in_combo = e.within(this.el);
18131 var in_list = e.within(this.list);
18132 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18134 if (in_combo || in_list || is_list) {
18135 //e.stopPropagation();
18140 this.onTickableFooterButtonClick(e, false, false);
18148 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18150 expand : function(){
18152 if(this.isExpanded() || !this.hasFocus){
18156 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18157 this.list.setWidth(lw);
18163 this.restrictHeight();
18167 this.tickItems = Roo.apply([], this.item);
18170 this.cancelBtn.show();
18171 this.trigger.hide();
18174 this.tickableInputEl().focus();
18179 Roo.get(document).on('mousedown', this.collapseIf, this);
18180 Roo.get(document).on('mousewheel', this.collapseIf, this);
18181 if (!this.editable) {
18182 Roo.get(document).on('keydown', this.listKeyPress, this);
18185 this.fireEvent('expand', this);
18189 // Implements the default empty TriggerField.onTriggerClick function
18190 onTriggerClick : function(e)
18192 Roo.log('trigger click');
18194 if(this.disabled || !this.triggerList){
18199 this.loadNext = false;
18201 if(this.isExpanded()){
18203 if (!this.blockFocus) {
18204 this.inputEl().focus();
18208 this.hasFocus = true;
18209 if(this.triggerAction == 'all') {
18210 this.doQuery(this.allQuery, true);
18212 this.doQuery(this.getRawValue());
18214 if (!this.blockFocus) {
18215 this.inputEl().focus();
18220 onTickableTriggerClick : function(e)
18227 this.loadNext = false;
18228 this.hasFocus = true;
18230 if(this.triggerAction == 'all') {
18231 this.doQuery(this.allQuery, true);
18233 this.doQuery(this.getRawValue());
18237 onSearchFieldClick : function(e)
18239 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18240 this.onTickableFooterButtonClick(e, false, false);
18244 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18249 this.loadNext = false;
18250 this.hasFocus = true;
18252 if(this.triggerAction == 'all') {
18253 this.doQuery(this.allQuery, true);
18255 this.doQuery(this.getRawValue());
18259 listKeyPress : function(e)
18261 //Roo.log('listkeypress');
18262 // scroll to first matching element based on key pres..
18263 if (e.isSpecialKey()) {
18266 var k = String.fromCharCode(e.getKey()).toUpperCase();
18269 var csel = this.view.getSelectedNodes();
18270 var cselitem = false;
18272 var ix = this.view.indexOf(csel[0]);
18273 cselitem = this.store.getAt(ix);
18274 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18280 this.store.each(function(v) {
18282 // start at existing selection.
18283 if (cselitem.id == v.id) {
18289 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18290 match = this.store.indexOf(v);
18296 if (match === false) {
18297 return true; // no more action?
18300 this.view.select(match);
18301 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18302 sn.scrollIntoView(sn.dom.parentNode, false);
18305 onViewScroll : function(e, t){
18307 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){
18311 this.hasQuery = true;
18313 this.loading = this.list.select('.loading', true).first();
18315 if(this.loading === null){
18316 this.list.createChild({
18318 cls: 'loading roo-select2-more-results roo-select2-active',
18319 html: 'Loading more results...'
18322 this.loading = this.list.select('.loading', true).first();
18324 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18326 this.loading.hide();
18329 this.loading.show();
18334 this.loadNext = true;
18336 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18341 addItem : function(o)
18343 var dv = ''; // display value
18345 if (this.displayField) {
18346 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18348 // this is an error condition!!!
18349 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18356 var choice = this.choices.createChild({
18358 cls: 'roo-select2-search-choice',
18367 cls: 'roo-select2-search-choice-close fa fa-times',
18372 }, this.searchField);
18374 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18376 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18384 this.inputEl().dom.value = '';
18389 onRemoveItem : function(e, _self, o)
18391 e.preventDefault();
18393 this.lastItem = Roo.apply([], this.item);
18395 var index = this.item.indexOf(o.data) * 1;
18398 Roo.log('not this item?!');
18402 this.item.splice(index, 1);
18407 this.fireEvent('remove', this, e);
18413 syncValue : function()
18415 if(!this.item.length){
18422 Roo.each(this.item, function(i){
18423 if(_this.valueField){
18424 value.push(i[_this.valueField]);
18431 this.value = value.join(',');
18433 if(this.hiddenField){
18434 this.hiddenField.dom.value = this.value;
18437 this.store.fireEvent("datachanged", this.store);
18442 clearItem : function()
18444 if(!this.multiple){
18450 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18458 if(this.tickable && !Roo.isTouch){
18459 this.view.refresh();
18463 inputEl: function ()
18465 if(Roo.isIOS && this.useNativeIOS){
18466 return this.el.select('select.roo-ios-select', true).first();
18469 if(Roo.isTouch && this.mobileTouchView){
18470 return this.el.select('input.form-control',true).first();
18474 return this.searchField;
18477 return this.el.select('input.form-control',true).first();
18480 onTickableFooterButtonClick : function(e, btn, el)
18482 e.preventDefault();
18484 this.lastItem = Roo.apply([], this.item);
18486 if(btn && btn.name == 'cancel'){
18487 this.tickItems = Roo.apply([], this.item);
18496 Roo.each(this.tickItems, function(o){
18504 validate : function()
18506 if(this.getVisibilityEl().hasClass('hidden')){
18510 var v = this.getRawValue();
18513 v = this.getValue();
18516 if(this.disabled || this.allowBlank || v.length){
18521 this.markInvalid();
18525 tickableInputEl : function()
18527 if(!this.tickable || !this.editable){
18528 return this.inputEl();
18531 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18535 getAutoCreateTouchView : function()
18540 cls: 'form-group' //input-group
18546 type : this.inputType,
18547 cls : 'form-control x-combo-noedit',
18548 autocomplete: 'new-password',
18549 placeholder : this.placeholder || '',
18554 input.name = this.name;
18558 input.cls += ' input-' + this.size;
18561 if (this.disabled) {
18562 input.disabled = true;
18566 cls : 'roo-combobox-wrap',
18573 inputblock.cls += ' input-group';
18575 inputblock.cn.unshift({
18577 cls : 'input-group-addon input-group-prepend input-group-text',
18582 if(this.removable && !this.multiple){
18583 inputblock.cls += ' roo-removable';
18585 inputblock.cn.push({
18588 cls : 'roo-combo-removable-btn close'
18592 if(this.hasFeedback && !this.allowBlank){
18594 inputblock.cls += ' has-feedback';
18596 inputblock.cn.push({
18598 cls: 'glyphicon form-control-feedback'
18605 inputblock.cls += (this.before) ? '' : ' input-group';
18607 inputblock.cn.push({
18609 cls : 'input-group-addon input-group-append input-group-text',
18615 var ibwrap = inputblock;
18620 cls: 'roo-select2-choices',
18624 cls: 'roo-select2-search-field',
18637 cls: 'roo-select2-container input-group roo-touchview-combobox ',
18642 cls: 'form-hidden-field'
18648 if(!this.multiple && this.showToggleBtn){
18654 if (this.caret != false) {
18657 cls: 'fa fa-' + this.caret
18664 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18666 Roo.bootstrap.version == 3 ? caret : '',
18669 cls: 'combobox-clear',
18683 combobox.cls += ' roo-select2-container-multi';
18686 var required = this.allowBlank ? {
18688 style: 'display: none'
18691 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18692 tooltip : 'This field is required'
18695 var align = this.labelAlign || this.parentLabelAlign();
18697 if (align ==='left' && this.fieldLabel.length) {
18703 cls : 'control-label col-form-label',
18704 html : this.fieldLabel
18708 cls : 'roo-combobox-wrap ',
18715 var labelCfg = cfg.cn[1];
18716 var contentCfg = cfg.cn[2];
18719 if(this.indicatorpos == 'right'){
18724 cls : 'control-label col-form-label',
18728 html : this.fieldLabel
18734 cls : "roo-combobox-wrap ",
18742 labelCfg = cfg.cn[0];
18743 contentCfg = cfg.cn[1];
18748 if(this.labelWidth > 12){
18749 labelCfg.style = "width: " + this.labelWidth + 'px';
18752 if(this.labelWidth < 13 && this.labelmd == 0){
18753 this.labelmd = this.labelWidth;
18756 if(this.labellg > 0){
18757 labelCfg.cls += ' col-lg-' + this.labellg;
18758 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18761 if(this.labelmd > 0){
18762 labelCfg.cls += ' col-md-' + this.labelmd;
18763 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18766 if(this.labelsm > 0){
18767 labelCfg.cls += ' col-sm-' + this.labelsm;
18768 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18771 if(this.labelxs > 0){
18772 labelCfg.cls += ' col-xs-' + this.labelxs;
18773 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18777 } else if ( this.fieldLabel.length) {
18782 cls : 'control-label',
18783 html : this.fieldLabel
18794 if(this.indicatorpos == 'right'){
18798 cls : 'control-label',
18799 html : this.fieldLabel,
18817 var settings = this;
18819 ['xs','sm','md','lg'].map(function(size){
18820 if (settings[size]) {
18821 cfg.cls += ' col-' + size + '-' + settings[size];
18828 initTouchView : function()
18830 this.renderTouchView();
18832 this.touchViewEl.on('scroll', function(){
18833 this.el.dom.scrollTop = 0;
18836 this.originalValue = this.getValue();
18838 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18840 this.inputEl().on("click", this.showTouchView, this);
18841 if (this.triggerEl) {
18842 this.triggerEl.on("click", this.showTouchView, this);
18846 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18847 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18849 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18851 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18852 this.store.on('load', this.onTouchViewLoad, this);
18853 this.store.on('loadexception', this.onTouchViewLoadException, this);
18855 if(this.hiddenName){
18857 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18859 this.hiddenField.dom.value =
18860 this.hiddenValue !== undefined ? this.hiddenValue :
18861 this.value !== undefined ? this.value : '';
18863 this.el.dom.removeAttribute('name');
18864 this.hiddenField.dom.setAttribute('name', this.hiddenName);
18868 this.choices = this.el.select('ul.roo-select2-choices', true).first();
18869 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18872 if(this.removable && !this.multiple){
18873 var close = this.closeTriggerEl();
18875 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18876 close.on('click', this.removeBtnClick, this, close);
18880 * fix the bug in Safari iOS8
18882 this.inputEl().on("focus", function(e){
18883 document.activeElement.blur();
18886 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18893 renderTouchView : function()
18895 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18896 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18898 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18899 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18901 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18902 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18903 this.touchViewBodyEl.setStyle('overflow', 'auto');
18905 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18906 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18908 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18909 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18913 showTouchView : function()
18919 this.touchViewHeaderEl.hide();
18921 if(this.modalTitle.length){
18922 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18923 this.touchViewHeaderEl.show();
18926 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18927 this.touchViewEl.show();
18929 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18931 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18932 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18934 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18936 if(this.modalTitle.length){
18937 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18940 this.touchViewBodyEl.setHeight(bodyHeight);
18944 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18946 this.touchViewEl.addClass(['in','show']);
18949 if(this._touchViewMask){
18950 Roo.get(document.body).addClass("x-body-masked");
18951 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18952 this._touchViewMask.setStyle('z-index', 10000);
18953 this._touchViewMask.addClass('show');
18956 this.doTouchViewQuery();
18960 hideTouchView : function()
18962 this.touchViewEl.removeClass(['in','show']);
18966 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18968 this.touchViewEl.setStyle('display', 'none');
18971 if(this._touchViewMask){
18972 this._touchViewMask.removeClass('show');
18973 Roo.get(document.body).removeClass("x-body-masked");
18977 setTouchViewValue : function()
18984 Roo.each(this.tickItems, function(o){
18989 this.hideTouchView();
18992 doTouchViewQuery : function()
19001 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19005 if(!this.alwaysQuery || this.mode == 'local'){
19006 this.onTouchViewLoad();
19013 onTouchViewBeforeLoad : function(combo,opts)
19019 onTouchViewLoad : function()
19021 if(this.store.getCount() < 1){
19022 this.onTouchViewEmptyResults();
19026 this.clearTouchView();
19028 var rawValue = this.getRawValue();
19030 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19032 this.tickItems = [];
19034 this.store.data.each(function(d, rowIndex){
19035 var row = this.touchViewListGroup.createChild(template);
19037 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19038 row.addClass(d.data.cls);
19041 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19044 html : d.data[this.displayField]
19047 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19048 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19051 row.removeClass('selected');
19052 if(!this.multiple && this.valueField &&
19053 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19056 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19057 row.addClass('selected');
19060 if(this.multiple && this.valueField &&
19061 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19065 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19066 this.tickItems.push(d.data);
19069 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19073 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19075 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19077 if(this.modalTitle.length){
19078 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19081 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19083 if(this.mobile_restrict_height && listHeight < bodyHeight){
19084 this.touchViewBodyEl.setHeight(listHeight);
19089 if(firstChecked && listHeight > bodyHeight){
19090 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19095 onTouchViewLoadException : function()
19097 this.hideTouchView();
19100 onTouchViewEmptyResults : function()
19102 this.clearTouchView();
19104 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19106 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19110 clearTouchView : function()
19112 this.touchViewListGroup.dom.innerHTML = '';
19115 onTouchViewClick : function(e, el, o)
19117 e.preventDefault();
19120 var rowIndex = o.rowIndex;
19122 var r = this.store.getAt(rowIndex);
19124 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19126 if(!this.multiple){
19127 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19128 c.dom.removeAttribute('checked');
19131 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19133 this.setFromData(r.data);
19135 var close = this.closeTriggerEl();
19141 this.hideTouchView();
19143 this.fireEvent('select', this, r, rowIndex);
19148 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19149 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19150 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19154 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19155 this.addItem(r.data);
19156 this.tickItems.push(r.data);
19160 getAutoCreateNativeIOS : function()
19163 cls: 'form-group' //input-group,
19168 cls : 'roo-ios-select'
19172 combobox.name = this.name;
19175 if (this.disabled) {
19176 combobox.disabled = true;
19179 var settings = this;
19181 ['xs','sm','md','lg'].map(function(size){
19182 if (settings[size]) {
19183 cfg.cls += ' col-' + size + '-' + settings[size];
19193 initIOSView : function()
19195 this.store.on('load', this.onIOSViewLoad, this);
19200 onIOSViewLoad : function()
19202 if(this.store.getCount() < 1){
19206 this.clearIOSView();
19208 if(this.allowBlank) {
19210 var default_text = '-- SELECT --';
19212 if(this.placeholder.length){
19213 default_text = this.placeholder;
19216 if(this.emptyTitle.length){
19217 default_text += ' - ' + this.emptyTitle + ' -';
19220 var opt = this.inputEl().createChild({
19223 html : default_text
19227 o[this.valueField] = 0;
19228 o[this.displayField] = default_text;
19230 this.ios_options.push({
19237 this.store.data.each(function(d, rowIndex){
19241 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19242 html = d.data[this.displayField];
19247 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19248 value = d.data[this.valueField];
19257 if(this.value == d.data[this.valueField]){
19258 option['selected'] = true;
19261 var opt = this.inputEl().createChild(option);
19263 this.ios_options.push({
19270 this.inputEl().on('change', function(){
19271 this.fireEvent('select', this);
19276 clearIOSView: function()
19278 this.inputEl().dom.innerHTML = '';
19280 this.ios_options = [];
19283 setIOSValue: function(v)
19287 if(!this.ios_options){
19291 Roo.each(this.ios_options, function(opts){
19293 opts.el.dom.removeAttribute('selected');
19295 if(opts.data[this.valueField] != v){
19299 opts.el.dom.setAttribute('selected', true);
19305 * @cfg {Boolean} grow
19309 * @cfg {Number} growMin
19313 * @cfg {Number} growMax
19322 Roo.apply(Roo.bootstrap.ComboBox, {
19326 cls: 'modal-header',
19348 cls: 'list-group-item',
19352 cls: 'roo-combobox-list-group-item-value'
19356 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19370 listItemCheckbox : {
19372 cls: 'list-group-item',
19376 cls: 'roo-combobox-list-group-item-value'
19380 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19396 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19401 cls: 'modal-footer',
19409 cls: 'col-xs-6 text-left',
19412 cls: 'btn btn-danger roo-touch-view-cancel',
19418 cls: 'col-xs-6 text-right',
19421 cls: 'btn btn-success roo-touch-view-ok',
19432 Roo.apply(Roo.bootstrap.ComboBox, {
19434 touchViewTemplate : {
19436 cls: 'modal fade roo-combobox-touch-view',
19440 cls: 'modal-dialog',
19441 style : 'position:fixed', // we have to fix position....
19445 cls: 'modal-content',
19447 Roo.bootstrap.ComboBox.header,
19448 Roo.bootstrap.ComboBox.body,
19449 Roo.bootstrap.ComboBox.footer
19458 * Ext JS Library 1.1.1
19459 * Copyright(c) 2006-2007, Ext JS, LLC.
19461 * Originally Released Under LGPL - original licence link has changed is not relivant.
19464 * <script type="text/javascript">
19469 * @extends Roo.util.Observable
19470 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19471 * This class also supports single and multi selection modes. <br>
19472 * Create a data model bound view:
19474 var store = new Roo.data.Store(...);
19476 var view = new Roo.View({
19478 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19480 singleSelect: true,
19481 selectedClass: "ydataview-selected",
19485 // listen for node click?
19486 view.on("click", function(vw, index, node, e){
19487 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19491 dataModel.load("foobar.xml");
19493 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19495 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19496 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19498 * Note: old style constructor is still suported (container, template, config)
19501 * Create a new View
19502 * @param {Object} config The config object
19505 Roo.View = function(config, depreciated_tpl, depreciated_config){
19507 this.parent = false;
19509 if (typeof(depreciated_tpl) == 'undefined') {
19510 // new way.. - universal constructor.
19511 Roo.apply(this, config);
19512 this.el = Roo.get(this.el);
19515 this.el = Roo.get(config);
19516 this.tpl = depreciated_tpl;
19517 Roo.apply(this, depreciated_config);
19519 this.wrapEl = this.el.wrap().wrap();
19520 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19523 if(typeof(this.tpl) == "string"){
19524 this.tpl = new Roo.Template(this.tpl);
19526 // support xtype ctors..
19527 this.tpl = new Roo.factory(this.tpl, Roo);
19531 this.tpl.compile();
19536 * @event beforeclick
19537 * Fires before a click is processed. Returns false to cancel the default action.
19538 * @param {Roo.View} this
19539 * @param {Number} index The index of the target node
19540 * @param {HTMLElement} node The target node
19541 * @param {Roo.EventObject} e The raw event object
19543 "beforeclick" : true,
19546 * Fires when a template node is clicked.
19547 * @param {Roo.View} this
19548 * @param {Number} index The index of the target node
19549 * @param {HTMLElement} node The target node
19550 * @param {Roo.EventObject} e The raw event object
19555 * Fires when a template node is double clicked.
19556 * @param {Roo.View} this
19557 * @param {Number} index The index of the target node
19558 * @param {HTMLElement} node The target node
19559 * @param {Roo.EventObject} e The raw event object
19563 * @event contextmenu
19564 * Fires when a template node is right clicked.
19565 * @param {Roo.View} this
19566 * @param {Number} index The index of the target node
19567 * @param {HTMLElement} node The target node
19568 * @param {Roo.EventObject} e The raw event object
19570 "contextmenu" : true,
19572 * @event selectionchange
19573 * Fires when the selected nodes change.
19574 * @param {Roo.View} this
19575 * @param {Array} selections Array of the selected nodes
19577 "selectionchange" : true,
19580 * @event beforeselect
19581 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19582 * @param {Roo.View} this
19583 * @param {HTMLElement} node The node to be selected
19584 * @param {Array} selections Array of currently selected nodes
19586 "beforeselect" : true,
19588 * @event preparedata
19589 * Fires on every row to render, to allow you to change the data.
19590 * @param {Roo.View} this
19591 * @param {Object} data to be rendered (change this)
19593 "preparedata" : true
19601 "click": this.onClick,
19602 "dblclick": this.onDblClick,
19603 "contextmenu": this.onContextMenu,
19607 this.selections = [];
19609 this.cmp = new Roo.CompositeElementLite([]);
19611 this.store = Roo.factory(this.store, Roo.data);
19612 this.setStore(this.store, true);
19615 if ( this.footer && this.footer.xtype) {
19617 var fctr = this.wrapEl.appendChild(document.createElement("div"));
19619 this.footer.dataSource = this.store;
19620 this.footer.container = fctr;
19621 this.footer = Roo.factory(this.footer, Roo);
19622 fctr.insertFirst(this.el);
19624 // this is a bit insane - as the paging toolbar seems to detach the el..
19625 // dom.parentNode.parentNode.parentNode
19626 // they get detached?
19630 Roo.View.superclass.constructor.call(this);
19635 Roo.extend(Roo.View, Roo.util.Observable, {
19638 * @cfg {Roo.data.Store} store Data store to load data from.
19643 * @cfg {String|Roo.Element} el The container element.
19648 * @cfg {String|Roo.Template} tpl The template used by this View
19652 * @cfg {String} dataName the named area of the template to use as the data area
19653 * Works with domtemplates roo-name="name"
19657 * @cfg {String} selectedClass The css class to add to selected nodes
19659 selectedClass : "x-view-selected",
19661 * @cfg {String} emptyText The empty text to show when nothing is loaded.
19666 * @cfg {String} text to display on mask (default Loading)
19670 * @cfg {Boolean} multiSelect Allow multiple selection
19672 multiSelect : false,
19674 * @cfg {Boolean} singleSelect Allow single selection
19676 singleSelect: false,
19679 * @cfg {Boolean} toggleSelect - selecting
19681 toggleSelect : false,
19684 * @cfg {Boolean} tickable - selecting
19689 * Returns the element this view is bound to.
19690 * @return {Roo.Element}
19692 getEl : function(){
19693 return this.wrapEl;
19699 * Refreshes the view. - called by datachanged on the store. - do not call directly.
19701 refresh : function(){
19702 //Roo.log('refresh');
19705 // if we are using something like 'domtemplate', then
19706 // the what gets used is:
19707 // t.applySubtemplate(NAME, data, wrapping data..)
19708 // the outer template then get' applied with
19709 // the store 'extra data'
19710 // and the body get's added to the
19711 // roo-name="data" node?
19712 // <span class='roo-tpl-{name}'></span> ?????
19716 this.clearSelections();
19717 this.el.update("");
19719 var records = this.store.getRange();
19720 if(records.length < 1) {
19722 // is this valid?? = should it render a template??
19724 this.el.update(this.emptyText);
19728 if (this.dataName) {
19729 this.el.update(t.apply(this.store.meta)); //????
19730 el = this.el.child('.roo-tpl-' + this.dataName);
19733 for(var i = 0, len = records.length; i < len; i++){
19734 var data = this.prepareData(records[i].data, i, records[i]);
19735 this.fireEvent("preparedata", this, data, i, records[i]);
19737 var d = Roo.apply({}, data);
19740 Roo.apply(d, {'roo-id' : Roo.id()});
19744 Roo.each(this.parent.item, function(item){
19745 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19748 Roo.apply(d, {'roo-data-checked' : 'checked'});
19752 html[html.length] = Roo.util.Format.trim(
19754 t.applySubtemplate(this.dataName, d, this.store.meta) :
19761 el.update(html.join(""));
19762 this.nodes = el.dom.childNodes;
19763 this.updateIndexes(0);
19768 * Function to override to reformat the data that is sent to
19769 * the template for each node.
19770 * DEPRICATED - use the preparedata event handler.
19771 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19772 * a JSON object for an UpdateManager bound view).
19774 prepareData : function(data, index, record)
19776 this.fireEvent("preparedata", this, data, index, record);
19780 onUpdate : function(ds, record){
19781 // Roo.log('on update');
19782 this.clearSelections();
19783 var index = this.store.indexOf(record);
19784 var n = this.nodes[index];
19785 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19786 n.parentNode.removeChild(n);
19787 this.updateIndexes(index, index);
19793 onAdd : function(ds, records, index)
19795 //Roo.log(['on Add', ds, records, index] );
19796 this.clearSelections();
19797 if(this.nodes.length == 0){
19801 var n = this.nodes[index];
19802 for(var i = 0, len = records.length; i < len; i++){
19803 var d = this.prepareData(records[i].data, i, records[i]);
19805 this.tpl.insertBefore(n, d);
19808 this.tpl.append(this.el, d);
19811 this.updateIndexes(index);
19814 onRemove : function(ds, record, index){
19815 // Roo.log('onRemove');
19816 this.clearSelections();
19817 var el = this.dataName ?
19818 this.el.child('.roo-tpl-' + this.dataName) :
19821 el.dom.removeChild(this.nodes[index]);
19822 this.updateIndexes(index);
19826 * Refresh an individual node.
19827 * @param {Number} index
19829 refreshNode : function(index){
19830 this.onUpdate(this.store, this.store.getAt(index));
19833 updateIndexes : function(startIndex, endIndex){
19834 var ns = this.nodes;
19835 startIndex = startIndex || 0;
19836 endIndex = endIndex || ns.length - 1;
19837 for(var i = startIndex; i <= endIndex; i++){
19838 ns[i].nodeIndex = i;
19843 * Changes the data store this view uses and refresh the view.
19844 * @param {Store} store
19846 setStore : function(store, initial){
19847 if(!initial && this.store){
19848 this.store.un("datachanged", this.refresh);
19849 this.store.un("add", this.onAdd);
19850 this.store.un("remove", this.onRemove);
19851 this.store.un("update", this.onUpdate);
19852 this.store.un("clear", this.refresh);
19853 this.store.un("beforeload", this.onBeforeLoad);
19854 this.store.un("load", this.onLoad);
19855 this.store.un("loadexception", this.onLoad);
19859 store.on("datachanged", this.refresh, this);
19860 store.on("add", this.onAdd, this);
19861 store.on("remove", this.onRemove, this);
19862 store.on("update", this.onUpdate, this);
19863 store.on("clear", this.refresh, this);
19864 store.on("beforeload", this.onBeforeLoad, this);
19865 store.on("load", this.onLoad, this);
19866 store.on("loadexception", this.onLoad, this);
19874 * onbeforeLoad - masks the loading area.
19877 onBeforeLoad : function(store,opts)
19879 //Roo.log('onBeforeLoad');
19881 this.el.update("");
19883 this.el.mask(this.mask ? this.mask : "Loading" );
19885 onLoad : function ()
19892 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19893 * @param {HTMLElement} node
19894 * @return {HTMLElement} The template node
19896 findItemFromChild : function(node){
19897 var el = this.dataName ?
19898 this.el.child('.roo-tpl-' + this.dataName,true) :
19901 if(!node || node.parentNode == el){
19904 var p = node.parentNode;
19905 while(p && p != el){
19906 if(p.parentNode == el){
19915 onClick : function(e){
19916 var item = this.findItemFromChild(e.getTarget());
19918 var index = this.indexOf(item);
19919 if(this.onItemClick(item, index, e) !== false){
19920 this.fireEvent("click", this, index, item, e);
19923 this.clearSelections();
19928 onContextMenu : function(e){
19929 var item = this.findItemFromChild(e.getTarget());
19931 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19936 onDblClick : function(e){
19937 var item = this.findItemFromChild(e.getTarget());
19939 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19943 onItemClick : function(item, index, e)
19945 if(this.fireEvent("beforeclick", this, index, item, e) === false){
19948 if (this.toggleSelect) {
19949 var m = this.isSelected(item) ? 'unselect' : 'select';
19952 _t[m](item, true, false);
19955 if(this.multiSelect || this.singleSelect){
19956 if(this.multiSelect && e.shiftKey && this.lastSelection){
19957 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19959 this.select(item, this.multiSelect && e.ctrlKey);
19960 this.lastSelection = item;
19963 if(!this.tickable){
19964 e.preventDefault();
19972 * Get the number of selected nodes.
19975 getSelectionCount : function(){
19976 return this.selections.length;
19980 * Get the currently selected nodes.
19981 * @return {Array} An array of HTMLElements
19983 getSelectedNodes : function(){
19984 return this.selections;
19988 * Get the indexes of the selected nodes.
19991 getSelectedIndexes : function(){
19992 var indexes = [], s = this.selections;
19993 for(var i = 0, len = s.length; i < len; i++){
19994 indexes.push(s[i].nodeIndex);
20000 * Clear all selections
20001 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20003 clearSelections : function(suppressEvent){
20004 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20005 this.cmp.elements = this.selections;
20006 this.cmp.removeClass(this.selectedClass);
20007 this.selections = [];
20008 if(!suppressEvent){
20009 this.fireEvent("selectionchange", this, this.selections);
20015 * Returns true if the passed node is selected
20016 * @param {HTMLElement/Number} node The node or node index
20017 * @return {Boolean}
20019 isSelected : function(node){
20020 var s = this.selections;
20024 node = this.getNode(node);
20025 return s.indexOf(node) !== -1;
20030 * @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
20031 * @param {Boolean} keepExisting (optional) true to keep existing selections
20032 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20034 select : function(nodeInfo, keepExisting, suppressEvent){
20035 if(nodeInfo instanceof Array){
20037 this.clearSelections(true);
20039 for(var i = 0, len = nodeInfo.length; i < len; i++){
20040 this.select(nodeInfo[i], true, true);
20044 var node = this.getNode(nodeInfo);
20045 if(!node || this.isSelected(node)){
20046 return; // already selected.
20049 this.clearSelections(true);
20052 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20053 Roo.fly(node).addClass(this.selectedClass);
20054 this.selections.push(node);
20055 if(!suppressEvent){
20056 this.fireEvent("selectionchange", this, this.selections);
20064 * @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
20065 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20066 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20068 unselect : function(nodeInfo, keepExisting, suppressEvent)
20070 if(nodeInfo instanceof Array){
20071 Roo.each(this.selections, function(s) {
20072 this.unselect(s, nodeInfo);
20076 var node = this.getNode(nodeInfo);
20077 if(!node || !this.isSelected(node)){
20078 //Roo.log("not selected");
20079 return; // not selected.
20083 Roo.each(this.selections, function(s) {
20085 Roo.fly(node).removeClass(this.selectedClass);
20092 this.selections= ns;
20093 this.fireEvent("selectionchange", this, this.selections);
20097 * Gets a template node.
20098 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20099 * @return {HTMLElement} The node or null if it wasn't found
20101 getNode : function(nodeInfo){
20102 if(typeof nodeInfo == "string"){
20103 return document.getElementById(nodeInfo);
20104 }else if(typeof nodeInfo == "number"){
20105 return this.nodes[nodeInfo];
20111 * Gets a range template nodes.
20112 * @param {Number} startIndex
20113 * @param {Number} endIndex
20114 * @return {Array} An array of nodes
20116 getNodes : function(start, end){
20117 var ns = this.nodes;
20118 start = start || 0;
20119 end = typeof end == "undefined" ? ns.length - 1 : end;
20122 for(var i = start; i <= end; i++){
20126 for(var i = start; i >= end; i--){
20134 * Finds the index of the passed node
20135 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20136 * @return {Number} The index of the node or -1
20138 indexOf : function(node){
20139 node = this.getNode(node);
20140 if(typeof node.nodeIndex == "number"){
20141 return node.nodeIndex;
20143 var ns = this.nodes;
20144 for(var i = 0, len = ns.length; i < len; i++){
20155 * based on jquery fullcalendar
20159 Roo.bootstrap = Roo.bootstrap || {};
20161 * @class Roo.bootstrap.Calendar
20162 * @extends Roo.bootstrap.Component
20163 * Bootstrap Calendar class
20164 * @cfg {Boolean} loadMask (true|false) default false
20165 * @cfg {Object} header generate the user specific header of the calendar, default false
20168 * Create a new Container
20169 * @param {Object} config The config object
20174 Roo.bootstrap.Calendar = function(config){
20175 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20179 * Fires when a date is selected
20180 * @param {DatePicker} this
20181 * @param {Date} date The selected date
20185 * @event monthchange
20186 * Fires when the displayed month changes
20187 * @param {DatePicker} this
20188 * @param {Date} date The selected month
20190 'monthchange': true,
20192 * @event evententer
20193 * Fires when mouse over an event
20194 * @param {Calendar} this
20195 * @param {event} Event
20197 'evententer': true,
20199 * @event eventleave
20200 * Fires when the mouse leaves an
20201 * @param {Calendar} this
20204 'eventleave': true,
20206 * @event eventclick
20207 * Fires when the mouse click an
20208 * @param {Calendar} this
20217 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20220 * @cfg {Roo.data.Store} store
20221 * The data source for the calendar
20225 * @cfg {Number} startDay
20226 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20234 getAutoCreate : function(){
20237 var fc_button = function(name, corner, style, content ) {
20238 return Roo.apply({},{
20240 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20242 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20245 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20256 style : 'width:100%',
20263 cls : 'fc-header-left',
20265 fc_button('prev', 'left', 'arrow', '‹' ),
20266 fc_button('next', 'right', 'arrow', '›' ),
20267 { tag: 'span', cls: 'fc-header-space' },
20268 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20276 cls : 'fc-header-center',
20280 cls: 'fc-header-title',
20283 html : 'month / year'
20291 cls : 'fc-header-right',
20293 /* fc_button('month', 'left', '', 'month' ),
20294 fc_button('week', '', '', 'week' ),
20295 fc_button('day', 'right', '', 'day' )
20307 header = this.header;
20310 var cal_heads = function() {
20312 // fixme - handle this.
20314 for (var i =0; i < Date.dayNames.length; i++) {
20315 var d = Date.dayNames[i];
20318 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20319 html : d.substring(0,3)
20323 ret[0].cls += ' fc-first';
20324 ret[6].cls += ' fc-last';
20327 var cal_cell = function(n) {
20330 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20335 cls: 'fc-day-number',
20339 cls: 'fc-day-content',
20343 style: 'position: relative;' // height: 17px;
20355 var cal_rows = function() {
20358 for (var r = 0; r < 6; r++) {
20365 for (var i =0; i < Date.dayNames.length; i++) {
20366 var d = Date.dayNames[i];
20367 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20370 row.cn[0].cls+=' fc-first';
20371 row.cn[0].cn[0].style = 'min-height:90px';
20372 row.cn[6].cls+=' fc-last';
20376 ret[0].cls += ' fc-first';
20377 ret[4].cls += ' fc-prev-last';
20378 ret[5].cls += ' fc-last';
20385 cls: 'fc-border-separate',
20386 style : 'width:100%',
20394 cls : 'fc-first fc-last',
20412 cls : 'fc-content',
20413 style : "position: relative;",
20416 cls : 'fc-view fc-view-month fc-grid',
20417 style : 'position: relative',
20418 unselectable : 'on',
20421 cls : 'fc-event-container',
20422 style : 'position:absolute;z-index:8;top:0;left:0;'
20440 initEvents : function()
20443 throw "can not find store for calendar";
20449 style: "text-align:center",
20453 style: "background-color:white;width:50%;margin:250 auto",
20457 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20468 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20470 var size = this.el.select('.fc-content', true).first().getSize();
20471 this.maskEl.setSize(size.width, size.height);
20472 this.maskEl.enableDisplayMode("block");
20473 if(!this.loadMask){
20474 this.maskEl.hide();
20477 this.store = Roo.factory(this.store, Roo.data);
20478 this.store.on('load', this.onLoad, this);
20479 this.store.on('beforeload', this.onBeforeLoad, this);
20483 this.cells = this.el.select('.fc-day',true);
20484 //Roo.log(this.cells);
20485 this.textNodes = this.el.query('.fc-day-number');
20486 this.cells.addClassOnOver('fc-state-hover');
20488 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20489 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20490 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20491 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20493 this.on('monthchange', this.onMonthChange, this);
20495 this.update(new Date().clearTime());
20498 resize : function() {
20499 var sz = this.el.getSize();
20501 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20502 this.el.select('.fc-day-content div',true).setHeight(34);
20507 showPrevMonth : function(e){
20508 this.update(this.activeDate.add("mo", -1));
20510 showToday : function(e){
20511 this.update(new Date().clearTime());
20514 showNextMonth : function(e){
20515 this.update(this.activeDate.add("mo", 1));
20519 showPrevYear : function(){
20520 this.update(this.activeDate.add("y", -1));
20524 showNextYear : function(){
20525 this.update(this.activeDate.add("y", 1));
20530 update : function(date)
20532 var vd = this.activeDate;
20533 this.activeDate = date;
20534 // if(vd && this.el){
20535 // var t = date.getTime();
20536 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20537 // Roo.log('using add remove');
20539 // this.fireEvent('monthchange', this, date);
20541 // this.cells.removeClass("fc-state-highlight");
20542 // this.cells.each(function(c){
20543 // if(c.dateValue == t){
20544 // c.addClass("fc-state-highlight");
20545 // setTimeout(function(){
20546 // try{c.dom.firstChild.focus();}catch(e){}
20556 var days = date.getDaysInMonth();
20558 var firstOfMonth = date.getFirstDateOfMonth();
20559 var startingPos = firstOfMonth.getDay()-this.startDay;
20561 if(startingPos < this.startDay){
20565 var pm = date.add(Date.MONTH, -1);
20566 var prevStart = pm.getDaysInMonth()-startingPos;
20568 this.cells = this.el.select('.fc-day',true);
20569 this.textNodes = this.el.query('.fc-day-number');
20570 this.cells.addClassOnOver('fc-state-hover');
20572 var cells = this.cells.elements;
20573 var textEls = this.textNodes;
20575 Roo.each(cells, function(cell){
20576 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20579 days += startingPos;
20581 // convert everything to numbers so it's fast
20582 var day = 86400000;
20583 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20586 //Roo.log(prevStart);
20588 var today = new Date().clearTime().getTime();
20589 var sel = date.clearTime().getTime();
20590 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20591 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20592 var ddMatch = this.disabledDatesRE;
20593 var ddText = this.disabledDatesText;
20594 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20595 var ddaysText = this.disabledDaysText;
20596 var format = this.format;
20598 var setCellClass = function(cal, cell){
20602 //Roo.log('set Cell Class');
20604 var t = d.getTime();
20608 cell.dateValue = t;
20610 cell.className += " fc-today";
20611 cell.className += " fc-state-highlight";
20612 cell.title = cal.todayText;
20615 // disable highlight in other month..
20616 //cell.className += " fc-state-highlight";
20621 cell.className = " fc-state-disabled";
20622 cell.title = cal.minText;
20626 cell.className = " fc-state-disabled";
20627 cell.title = cal.maxText;
20631 if(ddays.indexOf(d.getDay()) != -1){
20632 cell.title = ddaysText;
20633 cell.className = " fc-state-disabled";
20636 if(ddMatch && format){
20637 var fvalue = d.dateFormat(format);
20638 if(ddMatch.test(fvalue)){
20639 cell.title = ddText.replace("%0", fvalue);
20640 cell.className = " fc-state-disabled";
20644 if (!cell.initialClassName) {
20645 cell.initialClassName = cell.dom.className;
20648 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20653 for(; i < startingPos; i++) {
20654 textEls[i].innerHTML = (++prevStart);
20655 d.setDate(d.getDate()+1);
20657 cells[i].className = "fc-past fc-other-month";
20658 setCellClass(this, cells[i]);
20663 for(; i < days; i++){
20664 intDay = i - startingPos + 1;
20665 textEls[i].innerHTML = (intDay);
20666 d.setDate(d.getDate()+1);
20668 cells[i].className = ''; // "x-date-active";
20669 setCellClass(this, cells[i]);
20673 for(; i < 42; i++) {
20674 textEls[i].innerHTML = (++extraDays);
20675 d.setDate(d.getDate()+1);
20677 cells[i].className = "fc-future fc-other-month";
20678 setCellClass(this, cells[i]);
20681 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20683 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20685 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20686 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20688 if(totalRows != 6){
20689 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20690 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20693 this.fireEvent('monthchange', this, date);
20697 if(!this.internalRender){
20698 var main = this.el.dom.firstChild;
20699 var w = main.offsetWidth;
20700 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20701 Roo.fly(main).setWidth(w);
20702 this.internalRender = true;
20703 // opera does not respect the auto grow header center column
20704 // then, after it gets a width opera refuses to recalculate
20705 // without a second pass
20706 if(Roo.isOpera && !this.secondPass){
20707 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20708 this.secondPass = true;
20709 this.update.defer(10, this, [date]);
20716 findCell : function(dt) {
20717 dt = dt.clearTime().getTime();
20719 this.cells.each(function(c){
20720 //Roo.log("check " +c.dateValue + '?=' + dt);
20721 if(c.dateValue == dt){
20731 findCells : function(ev) {
20732 var s = ev.start.clone().clearTime().getTime();
20734 var e= ev.end.clone().clearTime().getTime();
20737 this.cells.each(function(c){
20738 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20740 if(c.dateValue > e){
20743 if(c.dateValue < s){
20752 // findBestRow: function(cells)
20756 // for (var i =0 ; i < cells.length;i++) {
20757 // ret = Math.max(cells[i].rows || 0,ret);
20764 addItem : function(ev)
20766 // look for vertical location slot in
20767 var cells = this.findCells(ev);
20769 // ev.row = this.findBestRow(cells);
20771 // work out the location.
20775 for(var i =0; i < cells.length; i++) {
20777 cells[i].row = cells[0].row;
20780 cells[i].row = cells[i].row + 1;
20790 if (crow.start.getY() == cells[i].getY()) {
20792 crow.end = cells[i];
20809 cells[0].events.push(ev);
20811 this.calevents.push(ev);
20814 clearEvents: function() {
20816 if(!this.calevents){
20820 Roo.each(this.cells.elements, function(c){
20826 Roo.each(this.calevents, function(e) {
20827 Roo.each(e.els, function(el) {
20828 el.un('mouseenter' ,this.onEventEnter, this);
20829 el.un('mouseleave' ,this.onEventLeave, this);
20834 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20840 renderEvents: function()
20844 this.cells.each(function(c) {
20853 if(c.row != c.events.length){
20854 r = 4 - (4 - (c.row - c.events.length));
20857 c.events = ev.slice(0, r);
20858 c.more = ev.slice(r);
20860 if(c.more.length && c.more.length == 1){
20861 c.events.push(c.more.pop());
20864 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20868 this.cells.each(function(c) {
20870 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20873 for (var e = 0; e < c.events.length; e++){
20874 var ev = c.events[e];
20875 var rows = ev.rows;
20877 for(var i = 0; i < rows.length; i++) {
20879 // how many rows should it span..
20882 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20883 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20885 unselectable : "on",
20888 cls: 'fc-event-inner',
20892 // cls: 'fc-event-time',
20893 // html : cells.length > 1 ? '' : ev.time
20897 cls: 'fc-event-title',
20898 html : String.format('{0}', ev.title)
20905 cls: 'ui-resizable-handle ui-resizable-e',
20906 html : '  '
20913 cfg.cls += ' fc-event-start';
20915 if ((i+1) == rows.length) {
20916 cfg.cls += ' fc-event-end';
20919 var ctr = _this.el.select('.fc-event-container',true).first();
20920 var cg = ctr.createChild(cfg);
20922 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20923 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20925 var r = (c.more.length) ? 1 : 0;
20926 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20927 cg.setWidth(ebox.right - sbox.x -2);
20929 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20930 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20931 cg.on('click', _this.onEventClick, _this, ev);
20942 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20943 style : 'position: absolute',
20944 unselectable : "on",
20947 cls: 'fc-event-inner',
20951 cls: 'fc-event-title',
20959 cls: 'ui-resizable-handle ui-resizable-e',
20960 html : '  '
20966 var ctr = _this.el.select('.fc-event-container',true).first();
20967 var cg = ctr.createChild(cfg);
20969 var sbox = c.select('.fc-day-content',true).first().getBox();
20970 var ebox = c.select('.fc-day-content',true).first().getBox();
20972 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
20973 cg.setWidth(ebox.right - sbox.x -2);
20975 cg.on('click', _this.onMoreEventClick, _this, c.more);
20985 onEventEnter: function (e, el,event,d) {
20986 this.fireEvent('evententer', this, el, event);
20989 onEventLeave: function (e, el,event,d) {
20990 this.fireEvent('eventleave', this, el, event);
20993 onEventClick: function (e, el,event,d) {
20994 this.fireEvent('eventclick', this, el, event);
20997 onMonthChange: function () {
21001 onMoreEventClick: function(e, el, more)
21005 this.calpopover.placement = 'right';
21006 this.calpopover.setTitle('More');
21008 this.calpopover.setContent('');
21010 var ctr = this.calpopover.el.select('.popover-content', true).first();
21012 Roo.each(more, function(m){
21014 cls : 'fc-event-hori fc-event-draggable',
21017 var cg = ctr.createChild(cfg);
21019 cg.on('click', _this.onEventClick, _this, m);
21022 this.calpopover.show(el);
21027 onLoad: function ()
21029 this.calevents = [];
21032 if(this.store.getCount() > 0){
21033 this.store.data.each(function(d){
21036 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21037 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21038 time : d.data.start_time,
21039 title : d.data.title,
21040 description : d.data.description,
21041 venue : d.data.venue
21046 this.renderEvents();
21048 if(this.calevents.length && this.loadMask){
21049 this.maskEl.hide();
21053 onBeforeLoad: function()
21055 this.clearEvents();
21057 this.maskEl.show();
21071 * @class Roo.bootstrap.Popover
21072 * @extends Roo.bootstrap.Component
21074 * Bootstrap Popover class
21075 * @cfg {String} html contents of the popover (or false to use children..)
21076 * @cfg {String} title of popover (or false to hide)
21077 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21078 * @cfg {String} trigger click || hover (or false to trigger manually)
21079 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21080 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21081 * - if false and it has a 'parent' then it will be automatically added to that element
21082 * - if string - Roo.get will be called
21083 * @cfg {Number} delay - delay before showing
21086 * Create a new Popover
21087 * @param {Object} config The config object
21090 Roo.bootstrap.Popover = function(config){
21091 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21097 * After the popover show
21099 * @param {Roo.bootstrap.Popover} this
21104 * After the popover hide
21106 * @param {Roo.bootstrap.Popover} this
21112 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21117 placement : 'right',
21118 trigger : 'hover', // hover
21124 can_build_overlaid : false,
21126 maskEl : false, // the mask element
21129 alignEl : false, // when show is called with an element - this get's stored.
21131 getChildContainer : function()
21133 return this.contentEl;
21136 getPopoverHeader : function()
21138 this.title = true; // flag not to hide it..
21139 this.headerEl.addClass('p-0');
21140 return this.headerEl
21144 getAutoCreate : function(){
21147 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21148 style: 'display:block',
21154 cls : 'popover-inner ',
21158 cls: 'popover-title popover-header',
21159 html : this.title === false ? '' : this.title
21162 cls : 'popover-content popover-body ' + (this.cls || ''),
21163 html : this.html || ''
21174 * @param {string} the title
21176 setTitle: function(str)
21180 this.headerEl.dom.innerHTML = str;
21185 * @param {string} the body content
21187 setContent: function(str)
21190 if (this.contentEl) {
21191 this.contentEl.dom.innerHTML = str;
21195 // as it get's added to the bottom of the page.
21196 onRender : function(ct, position)
21198 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21203 var cfg = Roo.apply({}, this.getAutoCreate());
21207 cfg.cls += ' ' + this.cls;
21210 cfg.style = this.style;
21212 //Roo.log("adding to ");
21213 this.el = Roo.get(document.body).createChild(cfg, position);
21214 // Roo.log(this.el);
21217 this.contentEl = this.el.select('.popover-content',true).first();
21218 this.headerEl = this.el.select('.popover-title',true).first();
21221 if(typeof(this.items) != 'undefined'){
21222 var items = this.items;
21225 for(var i =0;i < items.length;i++) {
21226 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21230 this.items = nitems;
21232 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21233 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21240 resizeMask : function()
21242 this.maskEl.setSize(
21243 Roo.lib.Dom.getViewWidth(true),
21244 Roo.lib.Dom.getViewHeight(true)
21248 initEvents : function()
21252 Roo.bootstrap.Popover.register(this);
21255 this.arrowEl = this.el.select('.arrow',true).first();
21256 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21257 this.el.enableDisplayMode('block');
21261 if (this.over === false && !this.parent()) {
21264 if (this.triggers === false) {
21269 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21270 var triggers = this.trigger ? this.trigger.split(' ') : [];
21271 Roo.each(triggers, function(trigger) {
21273 if (trigger == 'click') {
21274 on_el.on('click', this.toggle, this);
21275 } else if (trigger != 'manual') {
21276 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21277 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21279 on_el.on(eventIn ,this.enter, this);
21280 on_el.on(eventOut, this.leave, this);
21290 toggle : function () {
21291 this.hoverState == 'in' ? this.leave() : this.enter();
21294 enter : function () {
21296 clearTimeout(this.timeout);
21298 this.hoverState = 'in';
21300 if (!this.delay || !this.delay.show) {
21305 this.timeout = setTimeout(function () {
21306 if (_t.hoverState == 'in') {
21309 }, this.delay.show)
21312 leave : function() {
21313 clearTimeout(this.timeout);
21315 this.hoverState = 'out';
21317 if (!this.delay || !this.delay.hide) {
21322 this.timeout = setTimeout(function () {
21323 if (_t.hoverState == 'out') {
21326 }, this.delay.hide)
21330 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21331 * @param {string} (left|right|top|bottom) position
21333 show : function (on_el, placement)
21335 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21336 on_el = on_el || false; // default to false
21339 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21340 on_el = this.parent().el;
21341 } else if (this.over) {
21342 on_el = Roo.get(this.over);
21347 this.alignEl = Roo.get( on_el );
21350 this.render(document.body);
21356 if (this.title === false) {
21357 this.headerEl.hide();
21362 this.el.dom.style.display = 'block';
21365 if (this.alignEl) {
21366 this.updatePosition(this.placement, true);
21369 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21370 var es = this.el.getSize();
21371 var x = Roo.lib.Dom.getViewWidth()/2;
21372 var y = Roo.lib.Dom.getViewHeight()/2;
21373 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21378 //var arrow = this.el.select('.arrow',true).first();
21379 //arrow.set(align[2],
21381 this.el.addClass('in');
21385 this.hoverState = 'in';
21388 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21389 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21390 this.maskEl.dom.style.display = 'block';
21391 this.maskEl.addClass('show');
21393 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21395 this.fireEvent('show', this);
21399 * fire this manually after loading a grid in the table for example
21400 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21401 * @param {Boolean} try and move it if we cant get right position.
21403 updatePosition : function(placement, try_move)
21405 // allow for calling with no parameters
21406 placement = placement ? placement : this.placement;
21407 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21409 this.el.removeClass([
21410 'fade','top','bottom', 'left', 'right','in',
21411 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21413 this.el.addClass(placement + ' bs-popover-' + placement);
21415 if (!this.alignEl ) {
21419 switch (placement) {
21421 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21422 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21423 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21424 //normal display... or moved up/down.
21425 this.el.setXY(offset);
21426 var xy = this.alignEl.getAnchorXY('tr', false);
21428 this.arrowEl.setXY(xy);
21431 // continue through...
21432 return this.updatePosition('left', false);
21436 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21437 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21438 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21439 //normal display... or moved up/down.
21440 this.el.setXY(offset);
21441 var xy = this.alignEl.getAnchorXY('tl', false);
21442 xy[0]-=10;xy[1]+=5; // << fix me
21443 this.arrowEl.setXY(xy);
21447 return this.updatePosition('right', false);
21450 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21451 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21452 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21453 //normal display... or moved up/down.
21454 this.el.setXY(offset);
21455 var xy = this.alignEl.getAnchorXY('t', false);
21456 xy[1]-=10; // << fix me
21457 this.arrowEl.setXY(xy);
21461 return this.updatePosition('bottom', false);
21464 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21465 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21466 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21467 //normal display... or moved up/down.
21468 this.el.setXY(offset);
21469 var xy = this.alignEl.getAnchorXY('b', false);
21470 xy[1]+=2; // << fix me
21471 this.arrowEl.setXY(xy);
21475 return this.updatePosition('top', false);
21486 this.el.setXY([0,0]);
21487 this.el.removeClass('in');
21489 this.hoverState = null;
21490 this.maskEl.hide(); // always..
21491 this.fireEvent('hide', this);
21497 Roo.apply(Roo.bootstrap.Popover, {
21500 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21501 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21502 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21503 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21508 clickHander : false,
21512 onMouseDown : function(e)
21514 if (this.popups.length && !e.getTarget(".roo-popover")) {
21515 /// what is nothing is showing..
21524 register : function(popup)
21526 if (!Roo.bootstrap.Popover.clickHandler) {
21527 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21529 // hide other popups.
21530 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21531 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21532 this.hideAll(); //<< why?
21533 //this.popups.push(popup);
21535 hideAll : function()
21537 this.popups.forEach(function(p) {
21541 onShow : function() {
21542 Roo.bootstrap.Popover.popups.push(this);
21544 onHide : function() {
21545 Roo.bootstrap.Popover.popups.remove(this);
21551 * Card header - holder for the card header elements.
21556 * @class Roo.bootstrap.PopoverNav
21557 * @extends Roo.bootstrap.NavGroup
21558 * Bootstrap Popover header navigation class
21560 * Create a new Popover Header Navigation
21561 * @param {Object} config The config object
21564 Roo.bootstrap.PopoverNav = function(config){
21565 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21568 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21571 container_method : 'getPopoverHeader'
21589 * @class Roo.bootstrap.Progress
21590 * @extends Roo.bootstrap.Component
21591 * Bootstrap Progress class
21592 * @cfg {Boolean} striped striped of the progress bar
21593 * @cfg {Boolean} active animated of the progress bar
21597 * Create a new Progress
21598 * @param {Object} config The config object
21601 Roo.bootstrap.Progress = function(config){
21602 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21605 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21610 getAutoCreate : function(){
21618 cfg.cls += ' progress-striped';
21622 cfg.cls += ' active';
21641 * @class Roo.bootstrap.ProgressBar
21642 * @extends Roo.bootstrap.Component
21643 * Bootstrap ProgressBar class
21644 * @cfg {Number} aria_valuenow aria-value now
21645 * @cfg {Number} aria_valuemin aria-value min
21646 * @cfg {Number} aria_valuemax aria-value max
21647 * @cfg {String} label label for the progress bar
21648 * @cfg {String} panel (success | info | warning | danger )
21649 * @cfg {String} role role of the progress bar
21650 * @cfg {String} sr_only text
21654 * Create a new ProgressBar
21655 * @param {Object} config The config object
21658 Roo.bootstrap.ProgressBar = function(config){
21659 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21662 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21666 aria_valuemax : 100,
21672 getAutoCreate : function()
21677 cls: 'progress-bar',
21678 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21690 cfg.role = this.role;
21693 if(this.aria_valuenow){
21694 cfg['aria-valuenow'] = this.aria_valuenow;
21697 if(this.aria_valuemin){
21698 cfg['aria-valuemin'] = this.aria_valuemin;
21701 if(this.aria_valuemax){
21702 cfg['aria-valuemax'] = this.aria_valuemax;
21705 if(this.label && !this.sr_only){
21706 cfg.html = this.label;
21710 cfg.cls += ' progress-bar-' + this.panel;
21716 update : function(aria_valuenow)
21718 this.aria_valuenow = aria_valuenow;
21720 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21735 * @class Roo.bootstrap.TabGroup
21736 * @extends Roo.bootstrap.Column
21737 * Bootstrap Column class
21738 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21739 * @cfg {Boolean} carousel true to make the group behave like a carousel
21740 * @cfg {Boolean} bullets show bullets for the panels
21741 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21742 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21743 * @cfg {Boolean} showarrow (true|false) show arrow default true
21746 * Create a new TabGroup
21747 * @param {Object} config The config object
21750 Roo.bootstrap.TabGroup = function(config){
21751 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21753 this.navId = Roo.id();
21756 Roo.bootstrap.TabGroup.register(this);
21760 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21763 transition : false,
21768 slideOnTouch : false,
21771 getAutoCreate : function()
21773 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21775 cfg.cls += ' tab-content';
21777 if (this.carousel) {
21778 cfg.cls += ' carousel slide';
21781 cls : 'carousel-inner',
21785 if(this.bullets && !Roo.isTouch){
21788 cls : 'carousel-bullets',
21792 if(this.bullets_cls){
21793 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21800 cfg.cn[0].cn.push(bullets);
21803 if(this.showarrow){
21804 cfg.cn[0].cn.push({
21806 class : 'carousel-arrow',
21810 class : 'carousel-prev',
21814 class : 'fa fa-chevron-left'
21820 class : 'carousel-next',
21824 class : 'fa fa-chevron-right'
21837 initEvents: function()
21839 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21840 // this.el.on("touchstart", this.onTouchStart, this);
21843 if(this.autoslide){
21846 this.slideFn = window.setInterval(function() {
21847 _this.showPanelNext();
21851 if(this.showarrow){
21852 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21853 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21859 // onTouchStart : function(e, el, o)
21861 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21865 // this.showPanelNext();
21869 getChildContainer : function()
21871 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21875 * register a Navigation item
21876 * @param {Roo.bootstrap.NavItem} the navitem to add
21878 register : function(item)
21880 this.tabs.push( item);
21881 item.navId = this.navId; // not really needed..
21886 getActivePanel : function()
21889 Roo.each(this.tabs, function(t) {
21899 getPanelByName : function(n)
21902 Roo.each(this.tabs, function(t) {
21903 if (t.tabId == n) {
21911 indexOfPanel : function(p)
21914 Roo.each(this.tabs, function(t,i) {
21915 if (t.tabId == p.tabId) {
21924 * show a specific panel
21925 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21926 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21928 showPanel : function (pan)
21930 if(this.transition || typeof(pan) == 'undefined'){
21931 Roo.log("waiting for the transitionend");
21935 if (typeof(pan) == 'number') {
21936 pan = this.tabs[pan];
21939 if (typeof(pan) == 'string') {
21940 pan = this.getPanelByName(pan);
21943 var cur = this.getActivePanel();
21946 Roo.log('pan or acitve pan is undefined');
21950 if (pan.tabId == this.getActivePanel().tabId) {
21954 if (false === cur.fireEvent('beforedeactivate')) {
21958 if(this.bullets > 0 && !Roo.isTouch){
21959 this.setActiveBullet(this.indexOfPanel(pan));
21962 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21964 //class="carousel-item carousel-item-next carousel-item-left"
21966 this.transition = true;
21967 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
21968 var lr = dir == 'next' ? 'left' : 'right';
21969 pan.el.addClass(dir); // or prev
21970 pan.el.addClass('carousel-item-' + dir); // or prev
21971 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21972 cur.el.addClass(lr); // or right
21973 pan.el.addClass(lr);
21974 cur.el.addClass('carousel-item-' +lr); // or right
21975 pan.el.addClass('carousel-item-' +lr);
21979 cur.el.on('transitionend', function() {
21980 Roo.log("trans end?");
21982 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21983 pan.setActive(true);
21985 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21986 cur.setActive(false);
21988 _this.transition = false;
21990 }, this, { single: true } );
21995 cur.setActive(false);
21996 pan.setActive(true);
22001 showPanelNext : function()
22003 var i = this.indexOfPanel(this.getActivePanel());
22005 if (i >= this.tabs.length - 1 && !this.autoslide) {
22009 if (i >= this.tabs.length - 1 && this.autoslide) {
22013 this.showPanel(this.tabs[i+1]);
22016 showPanelPrev : function()
22018 var i = this.indexOfPanel(this.getActivePanel());
22020 if (i < 1 && !this.autoslide) {
22024 if (i < 1 && this.autoslide) {
22025 i = this.tabs.length;
22028 this.showPanel(this.tabs[i-1]);
22032 addBullet: function()
22034 if(!this.bullets || Roo.isTouch){
22037 var ctr = this.el.select('.carousel-bullets',true).first();
22038 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22039 var bullet = ctr.createChild({
22040 cls : 'bullet bullet-' + i
22041 },ctr.dom.lastChild);
22046 bullet.on('click', (function(e, el, o, ii, t){
22048 e.preventDefault();
22050 this.showPanel(ii);
22052 if(this.autoslide && this.slideFn){
22053 clearInterval(this.slideFn);
22054 this.slideFn = window.setInterval(function() {
22055 _this.showPanelNext();
22059 }).createDelegate(this, [i, bullet], true));
22064 setActiveBullet : function(i)
22070 Roo.each(this.el.select('.bullet', true).elements, function(el){
22071 el.removeClass('selected');
22074 var bullet = this.el.select('.bullet-' + i, true).first();
22080 bullet.addClass('selected');
22091 Roo.apply(Roo.bootstrap.TabGroup, {
22095 * register a Navigation Group
22096 * @param {Roo.bootstrap.NavGroup} the navgroup to add
22098 register : function(navgrp)
22100 this.groups[navgrp.navId] = navgrp;
22104 * fetch a Navigation Group based on the navigation ID
22105 * if one does not exist , it will get created.
22106 * @param {string} the navgroup to add
22107 * @returns {Roo.bootstrap.NavGroup} the navgroup
22109 get: function(navId) {
22110 if (typeof(this.groups[navId]) == 'undefined') {
22111 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22113 return this.groups[navId] ;
22128 * @class Roo.bootstrap.TabPanel
22129 * @extends Roo.bootstrap.Component
22130 * Bootstrap TabPanel class
22131 * @cfg {Boolean} active panel active
22132 * @cfg {String} html panel content
22133 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22134 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22135 * @cfg {String} href click to link..
22136 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22140 * Create a new TabPanel
22141 * @param {Object} config The config object
22144 Roo.bootstrap.TabPanel = function(config){
22145 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22149 * Fires when the active status changes
22150 * @param {Roo.bootstrap.TabPanel} this
22151 * @param {Boolean} state the new state
22156 * @event beforedeactivate
22157 * Fires before a tab is de-activated - can be used to do validation on a form.
22158 * @param {Roo.bootstrap.TabPanel} this
22159 * @return {Boolean} false if there is an error
22162 'beforedeactivate': true
22165 this.tabId = this.tabId || Roo.id();
22169 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22176 touchSlide : false,
22177 getAutoCreate : function(){
22182 // item is needed for carousel - not sure if it has any effect otherwise
22183 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22184 html: this.html || ''
22188 cfg.cls += ' active';
22192 cfg.tabId = this.tabId;
22200 initEvents: function()
22202 var p = this.parent();
22204 this.navId = this.navId || p.navId;
22206 if (typeof(this.navId) != 'undefined') {
22207 // not really needed.. but just in case.. parent should be a NavGroup.
22208 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22212 var i = tg.tabs.length - 1;
22214 if(this.active && tg.bullets > 0 && i < tg.bullets){
22215 tg.setActiveBullet(i);
22219 this.el.on('click', this.onClick, this);
22221 if(Roo.isTouch && this.touchSlide){
22222 this.el.on("touchstart", this.onTouchStart, this);
22223 this.el.on("touchmove", this.onTouchMove, this);
22224 this.el.on("touchend", this.onTouchEnd, this);
22229 onRender : function(ct, position)
22231 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22234 setActive : function(state)
22236 Roo.log("panel - set active " + this.tabId + "=" + state);
22238 this.active = state;
22240 this.el.removeClass('active');
22242 } else if (!this.el.hasClass('active')) {
22243 this.el.addClass('active');
22246 this.fireEvent('changed', this, state);
22249 onClick : function(e)
22251 e.preventDefault();
22253 if(!this.href.length){
22257 window.location.href = this.href;
22266 onTouchStart : function(e)
22268 this.swiping = false;
22270 this.startX = e.browserEvent.touches[0].clientX;
22271 this.startY = e.browserEvent.touches[0].clientY;
22274 onTouchMove : function(e)
22276 this.swiping = true;
22278 this.endX = e.browserEvent.touches[0].clientX;
22279 this.endY = e.browserEvent.touches[0].clientY;
22282 onTouchEnd : function(e)
22289 var tabGroup = this.parent();
22291 if(this.endX > this.startX){ // swiping right
22292 tabGroup.showPanelPrev();
22296 if(this.startX > this.endX){ // swiping left
22297 tabGroup.showPanelNext();
22316 * @class Roo.bootstrap.DateField
22317 * @extends Roo.bootstrap.Input
22318 * Bootstrap DateField class
22319 * @cfg {Number} weekStart default 0
22320 * @cfg {String} viewMode default empty, (months|years)
22321 * @cfg {String} minViewMode default empty, (months|years)
22322 * @cfg {Number} startDate default -Infinity
22323 * @cfg {Number} endDate default Infinity
22324 * @cfg {Boolean} todayHighlight default false
22325 * @cfg {Boolean} todayBtn default false
22326 * @cfg {Boolean} calendarWeeks default false
22327 * @cfg {Object} daysOfWeekDisabled default empty
22328 * @cfg {Boolean} singleMode default false (true | false)
22330 * @cfg {Boolean} keyboardNavigation default true
22331 * @cfg {String} language default en
22334 * Create a new DateField
22335 * @param {Object} config The config object
22338 Roo.bootstrap.DateField = function(config){
22339 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22343 * Fires when this field show.
22344 * @param {Roo.bootstrap.DateField} this
22345 * @param {Mixed} date The date value
22350 * Fires when this field hide.
22351 * @param {Roo.bootstrap.DateField} this
22352 * @param {Mixed} date The date value
22357 * Fires when select a date.
22358 * @param {Roo.bootstrap.DateField} this
22359 * @param {Mixed} date The date value
22363 * @event beforeselect
22364 * Fires when before select a date.
22365 * @param {Roo.bootstrap.DateField} this
22366 * @param {Mixed} date The date value
22368 beforeselect : true
22372 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22375 * @cfg {String} format
22376 * The default date format string which can be overriden for localization support. The format must be
22377 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22381 * @cfg {String} altFormats
22382 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22383 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22385 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22393 todayHighlight : false,
22399 keyboardNavigation: true,
22401 calendarWeeks: false,
22403 startDate: -Infinity,
22407 daysOfWeekDisabled: [],
22411 singleMode : false,
22413 UTCDate: function()
22415 return new Date(Date.UTC.apply(Date, arguments));
22418 UTCToday: function()
22420 var today = new Date();
22421 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22424 getDate: function() {
22425 var d = this.getUTCDate();
22426 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22429 getUTCDate: function() {
22433 setDate: function(d) {
22434 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22437 setUTCDate: function(d) {
22439 this.setValue(this.formatDate(this.date));
22442 onRender: function(ct, position)
22445 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22447 this.language = this.language || 'en';
22448 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22449 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22451 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22452 this.format = this.format || 'm/d/y';
22453 this.isInline = false;
22454 this.isInput = true;
22455 this.component = this.el.select('.add-on', true).first() || false;
22456 this.component = (this.component && this.component.length === 0) ? false : this.component;
22457 this.hasInput = this.component && this.inputEl().length;
22459 if (typeof(this.minViewMode === 'string')) {
22460 switch (this.minViewMode) {
22462 this.minViewMode = 1;
22465 this.minViewMode = 2;
22468 this.minViewMode = 0;
22473 if (typeof(this.viewMode === 'string')) {
22474 switch (this.viewMode) {
22487 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22489 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22491 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22493 this.picker().on('mousedown', this.onMousedown, this);
22494 this.picker().on('click', this.onClick, this);
22496 this.picker().addClass('datepicker-dropdown');
22498 this.startViewMode = this.viewMode;
22500 if(this.singleMode){
22501 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22502 v.setVisibilityMode(Roo.Element.DISPLAY);
22506 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22507 v.setStyle('width', '189px');
22511 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22512 if(!this.calendarWeeks){
22517 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22518 v.attr('colspan', function(i, val){
22519 return parseInt(val) + 1;
22524 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22526 this.setStartDate(this.startDate);
22527 this.setEndDate(this.endDate);
22529 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22536 if(this.isInline) {
22541 picker : function()
22543 return this.pickerEl;
22544 // return this.el.select('.datepicker', true).first();
22547 fillDow: function()
22549 var dowCnt = this.weekStart;
22558 if(this.calendarWeeks){
22566 while (dowCnt < this.weekStart + 7) {
22570 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22574 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22577 fillMonths: function()
22580 var months = this.picker().select('>.datepicker-months td', true).first();
22582 months.dom.innerHTML = '';
22588 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22591 months.createChild(month);
22598 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;
22600 if (this.date < this.startDate) {
22601 this.viewDate = new Date(this.startDate);
22602 } else if (this.date > this.endDate) {
22603 this.viewDate = new Date(this.endDate);
22605 this.viewDate = new Date(this.date);
22613 var d = new Date(this.viewDate),
22614 year = d.getUTCFullYear(),
22615 month = d.getUTCMonth(),
22616 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22617 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22618 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22619 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22620 currentDate = this.date && this.date.valueOf(),
22621 today = this.UTCToday();
22623 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22625 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22627 // this.picker.select('>tfoot th.today').
22628 // .text(dates[this.language].today)
22629 // .toggle(this.todayBtn !== false);
22631 this.updateNavArrows();
22634 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22636 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22638 prevMonth.setUTCDate(day);
22640 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22642 var nextMonth = new Date(prevMonth);
22644 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22646 nextMonth = nextMonth.valueOf();
22648 var fillMonths = false;
22650 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22652 while(prevMonth.valueOf() <= nextMonth) {
22655 if (prevMonth.getUTCDay() === this.weekStart) {
22657 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22665 if(this.calendarWeeks){
22666 // ISO 8601: First week contains first thursday.
22667 // ISO also states week starts on Monday, but we can be more abstract here.
22669 // Start of current week: based on weekstart/current date
22670 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22671 // Thursday of this week
22672 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22673 // First Thursday of year, year from thursday
22674 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22675 // Calendar week: ms between thursdays, div ms per day, div 7 days
22676 calWeek = (th - yth) / 864e5 / 7 + 1;
22678 fillMonths.cn.push({
22686 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22688 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22691 if (this.todayHighlight &&
22692 prevMonth.getUTCFullYear() == today.getFullYear() &&
22693 prevMonth.getUTCMonth() == today.getMonth() &&
22694 prevMonth.getUTCDate() == today.getDate()) {
22695 clsName += ' today';
22698 if (currentDate && prevMonth.valueOf() === currentDate) {
22699 clsName += ' active';
22702 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22703 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22704 clsName += ' disabled';
22707 fillMonths.cn.push({
22709 cls: 'day ' + clsName,
22710 html: prevMonth.getDate()
22713 prevMonth.setDate(prevMonth.getDate()+1);
22716 var currentYear = this.date && this.date.getUTCFullYear();
22717 var currentMonth = this.date && this.date.getUTCMonth();
22719 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22721 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22722 v.removeClass('active');
22724 if(currentYear === year && k === currentMonth){
22725 v.addClass('active');
22728 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22729 v.addClass('disabled');
22735 year = parseInt(year/10, 10) * 10;
22737 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22739 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22742 for (var i = -1; i < 11; i++) {
22743 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22745 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22753 showMode: function(dir)
22756 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22759 Roo.each(this.picker().select('>div',true).elements, function(v){
22760 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22763 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22768 if(this.isInline) {
22772 this.picker().removeClass(['bottom', 'top']);
22774 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22776 * place to the top of element!
22780 this.picker().addClass('top');
22781 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22786 this.picker().addClass('bottom');
22788 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22791 parseDate : function(value)
22793 if(!value || value instanceof Date){
22796 var v = Date.parseDate(value, this.format);
22797 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22798 v = Date.parseDate(value, 'Y-m-d');
22800 if(!v && this.altFormats){
22801 if(!this.altFormatsArray){
22802 this.altFormatsArray = this.altFormats.split("|");
22804 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22805 v = Date.parseDate(value, this.altFormatsArray[i]);
22811 formatDate : function(date, fmt)
22813 return (!date || !(date instanceof Date)) ?
22814 date : date.dateFormat(fmt || this.format);
22817 onFocus : function()
22819 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22823 onBlur : function()
22825 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22827 var d = this.inputEl().getValue();
22834 showPopup : function()
22836 this.picker().show();
22840 this.fireEvent('showpopup', this, this.date);
22843 hidePopup : function()
22845 if(this.isInline) {
22848 this.picker().hide();
22849 this.viewMode = this.startViewMode;
22852 this.fireEvent('hidepopup', this, this.date);
22856 onMousedown: function(e)
22858 e.stopPropagation();
22859 e.preventDefault();
22864 Roo.bootstrap.DateField.superclass.keyup.call(this);
22868 setValue: function(v)
22870 if(this.fireEvent('beforeselect', this, v) !== false){
22871 var d = new Date(this.parseDate(v) ).clearTime();
22873 if(isNaN(d.getTime())){
22874 this.date = this.viewDate = '';
22875 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22879 v = this.formatDate(d);
22881 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22883 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22887 this.fireEvent('select', this, this.date);
22891 getValue: function()
22893 return this.formatDate(this.date);
22896 fireKey: function(e)
22898 if (!this.picker().isVisible()){
22899 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22905 var dateChanged = false,
22907 newDate, newViewDate;
22912 e.preventDefault();
22916 if (!this.keyboardNavigation) {
22919 dir = e.keyCode == 37 ? -1 : 1;
22922 newDate = this.moveYear(this.date, dir);
22923 newViewDate = this.moveYear(this.viewDate, dir);
22924 } else if (e.shiftKey){
22925 newDate = this.moveMonth(this.date, dir);
22926 newViewDate = this.moveMonth(this.viewDate, dir);
22928 newDate = new Date(this.date);
22929 newDate.setUTCDate(this.date.getUTCDate() + dir);
22930 newViewDate = new Date(this.viewDate);
22931 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22933 if (this.dateWithinRange(newDate)){
22934 this.date = newDate;
22935 this.viewDate = newViewDate;
22936 this.setValue(this.formatDate(this.date));
22938 e.preventDefault();
22939 dateChanged = true;
22944 if (!this.keyboardNavigation) {
22947 dir = e.keyCode == 38 ? -1 : 1;
22949 newDate = this.moveYear(this.date, dir);
22950 newViewDate = this.moveYear(this.viewDate, dir);
22951 } else if (e.shiftKey){
22952 newDate = this.moveMonth(this.date, dir);
22953 newViewDate = this.moveMonth(this.viewDate, dir);
22955 newDate = new Date(this.date);
22956 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22957 newViewDate = new Date(this.viewDate);
22958 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22960 if (this.dateWithinRange(newDate)){
22961 this.date = newDate;
22962 this.viewDate = newViewDate;
22963 this.setValue(this.formatDate(this.date));
22965 e.preventDefault();
22966 dateChanged = true;
22970 this.setValue(this.formatDate(this.date));
22972 e.preventDefault();
22975 this.setValue(this.formatDate(this.date));
22989 onClick: function(e)
22991 e.stopPropagation();
22992 e.preventDefault();
22994 var target = e.getTarget();
22996 if(target.nodeName.toLowerCase() === 'i'){
22997 target = Roo.get(target).dom.parentNode;
23000 var nodeName = target.nodeName;
23001 var className = target.className;
23002 var html = target.innerHTML;
23003 //Roo.log(nodeName);
23005 switch(nodeName.toLowerCase()) {
23007 switch(className) {
23013 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23014 switch(this.viewMode){
23016 this.viewDate = this.moveMonth(this.viewDate, dir);
23020 this.viewDate = this.moveYear(this.viewDate, dir);
23026 var date = new Date();
23027 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23029 this.setValue(this.formatDate(this.date));
23036 if (className.indexOf('disabled') < 0) {
23037 if (!this.viewDate) {
23038 this.viewDate = new Date();
23040 this.viewDate.setUTCDate(1);
23041 if (className.indexOf('month') > -1) {
23042 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23044 var year = parseInt(html, 10) || 0;
23045 this.viewDate.setUTCFullYear(year);
23049 if(this.singleMode){
23050 this.setValue(this.formatDate(this.viewDate));
23061 //Roo.log(className);
23062 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23063 var day = parseInt(html, 10) || 1;
23064 var year = (this.viewDate || new Date()).getUTCFullYear(),
23065 month = (this.viewDate || new Date()).getUTCMonth();
23067 if (className.indexOf('old') > -1) {
23074 } else if (className.indexOf('new') > -1) {
23082 //Roo.log([year,month,day]);
23083 this.date = this.UTCDate(year, month, day,0,0,0,0);
23084 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23086 //Roo.log(this.formatDate(this.date));
23087 this.setValue(this.formatDate(this.date));
23094 setStartDate: function(startDate)
23096 this.startDate = startDate || -Infinity;
23097 if (this.startDate !== -Infinity) {
23098 this.startDate = this.parseDate(this.startDate);
23101 this.updateNavArrows();
23104 setEndDate: function(endDate)
23106 this.endDate = endDate || Infinity;
23107 if (this.endDate !== Infinity) {
23108 this.endDate = this.parseDate(this.endDate);
23111 this.updateNavArrows();
23114 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23116 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23117 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23118 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23120 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23121 return parseInt(d, 10);
23124 this.updateNavArrows();
23127 updateNavArrows: function()
23129 if(this.singleMode){
23133 var d = new Date(this.viewDate),
23134 year = d.getUTCFullYear(),
23135 month = d.getUTCMonth();
23137 Roo.each(this.picker().select('.prev', true).elements, function(v){
23139 switch (this.viewMode) {
23142 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23148 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23155 Roo.each(this.picker().select('.next', true).elements, function(v){
23157 switch (this.viewMode) {
23160 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23166 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23174 moveMonth: function(date, dir)
23179 var new_date = new Date(date.valueOf()),
23180 day = new_date.getUTCDate(),
23181 month = new_date.getUTCMonth(),
23182 mag = Math.abs(dir),
23184 dir = dir > 0 ? 1 : -1;
23187 // If going back one month, make sure month is not current month
23188 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23190 return new_date.getUTCMonth() == month;
23192 // If going forward one month, make sure month is as expected
23193 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23195 return new_date.getUTCMonth() != new_month;
23197 new_month = month + dir;
23198 new_date.setUTCMonth(new_month);
23199 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23200 if (new_month < 0 || new_month > 11) {
23201 new_month = (new_month + 12) % 12;
23204 // For magnitudes >1, move one month at a time...
23205 for (var i=0; i<mag; i++) {
23206 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23207 new_date = this.moveMonth(new_date, dir);
23209 // ...then reset the day, keeping it in the new month
23210 new_month = new_date.getUTCMonth();
23211 new_date.setUTCDate(day);
23213 return new_month != new_date.getUTCMonth();
23216 // Common date-resetting loop -- if date is beyond end of month, make it
23219 new_date.setUTCDate(--day);
23220 new_date.setUTCMonth(new_month);
23225 moveYear: function(date, dir)
23227 return this.moveMonth(date, dir*12);
23230 dateWithinRange: function(date)
23232 return date >= this.startDate && date <= this.endDate;
23238 this.picker().remove();
23241 validateValue : function(value)
23243 if(this.getVisibilityEl().hasClass('hidden')){
23247 if(value.length < 1) {
23248 if(this.allowBlank){
23254 if(value.length < this.minLength){
23257 if(value.length > this.maxLength){
23261 var vt = Roo.form.VTypes;
23262 if(!vt[this.vtype](value, this)){
23266 if(typeof this.validator == "function"){
23267 var msg = this.validator(value);
23273 if(this.regex && !this.regex.test(value)){
23277 if(typeof(this.parseDate(value)) == 'undefined'){
23281 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23285 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23295 this.date = this.viewDate = '';
23297 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23302 Roo.apply(Roo.bootstrap.DateField, {
23313 html: '<i class="fa fa-arrow-left"/>'
23323 html: '<i class="fa fa-arrow-right"/>'
23365 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23366 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23367 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23368 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23369 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23382 navFnc: 'FullYear',
23387 navFnc: 'FullYear',
23392 Roo.apply(Roo.bootstrap.DateField, {
23396 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23400 cls: 'datepicker-days',
23404 cls: 'table-condensed',
23406 Roo.bootstrap.DateField.head,
23410 Roo.bootstrap.DateField.footer
23417 cls: 'datepicker-months',
23421 cls: 'table-condensed',
23423 Roo.bootstrap.DateField.head,
23424 Roo.bootstrap.DateField.content,
23425 Roo.bootstrap.DateField.footer
23432 cls: 'datepicker-years',
23436 cls: 'table-condensed',
23438 Roo.bootstrap.DateField.head,
23439 Roo.bootstrap.DateField.content,
23440 Roo.bootstrap.DateField.footer
23459 * @class Roo.bootstrap.TimeField
23460 * @extends Roo.bootstrap.Input
23461 * Bootstrap DateField class
23465 * Create a new TimeField
23466 * @param {Object} config The config object
23469 Roo.bootstrap.TimeField = function(config){
23470 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23474 * Fires when this field show.
23475 * @param {Roo.bootstrap.DateField} thisthis
23476 * @param {Mixed} date The date value
23481 * Fires when this field hide.
23482 * @param {Roo.bootstrap.DateField} this
23483 * @param {Mixed} date The date value
23488 * Fires when select a date.
23489 * @param {Roo.bootstrap.DateField} this
23490 * @param {Mixed} date The date value
23496 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23499 * @cfg {String} format
23500 * The default time format string which can be overriden for localization support. The format must be
23501 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23505 getAutoCreate : function()
23507 this.after = '<i class="fa far fa-clock"></i>';
23508 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23512 onRender: function(ct, position)
23515 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23517 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23519 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23521 this.pop = this.picker().select('>.datepicker-time',true).first();
23522 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23524 this.picker().on('mousedown', this.onMousedown, this);
23525 this.picker().on('click', this.onClick, this);
23527 this.picker().addClass('datepicker-dropdown');
23532 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23533 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23534 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23535 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23536 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23537 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23541 fireKey: function(e){
23542 if (!this.picker().isVisible()){
23543 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23549 e.preventDefault();
23557 this.onTogglePeriod();
23560 this.onIncrementMinutes();
23563 this.onDecrementMinutes();
23572 onClick: function(e) {
23573 e.stopPropagation();
23574 e.preventDefault();
23577 picker : function()
23579 return this.pickerEl;
23582 fillTime: function()
23584 var time = this.pop.select('tbody', true).first();
23586 time.dom.innerHTML = '';
23601 cls: 'hours-up fa fas fa-chevron-up'
23621 cls: 'minutes-up fa fas fa-chevron-up'
23642 cls: 'timepicker-hour',
23657 cls: 'timepicker-minute',
23672 cls: 'btn btn-primary period',
23694 cls: 'hours-down fa fas fa-chevron-down'
23714 cls: 'minutes-down fa fas fa-chevron-down'
23732 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23739 var hours = this.time.getHours();
23740 var minutes = this.time.getMinutes();
23753 hours = hours - 12;
23757 hours = '0' + hours;
23761 minutes = '0' + minutes;
23764 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23765 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23766 this.pop.select('button', true).first().dom.innerHTML = period;
23772 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23774 var cls = ['bottom'];
23776 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23783 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23787 //this.picker().setXY(20000,20000);
23788 this.picker().addClass(cls.join('-'));
23792 Roo.each(cls, function(c){
23797 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23798 //_this.picker().setTop(_this.inputEl().getHeight());
23802 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23804 //_this.picker().setTop(0 - _this.picker().getHeight());
23809 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23813 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23821 onFocus : function()
23823 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23827 onBlur : function()
23829 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23835 this.picker().show();
23840 this.fireEvent('show', this, this.date);
23845 this.picker().hide();
23848 this.fireEvent('hide', this, this.date);
23851 setTime : function()
23854 this.setValue(this.time.format(this.format));
23856 this.fireEvent('select', this, this.date);
23861 onMousedown: function(e){
23862 e.stopPropagation();
23863 e.preventDefault();
23866 onIncrementHours: function()
23868 Roo.log('onIncrementHours');
23869 this.time = this.time.add(Date.HOUR, 1);
23874 onDecrementHours: function()
23876 Roo.log('onDecrementHours');
23877 this.time = this.time.add(Date.HOUR, -1);
23881 onIncrementMinutes: function()
23883 Roo.log('onIncrementMinutes');
23884 this.time = this.time.add(Date.MINUTE, 1);
23888 onDecrementMinutes: function()
23890 Roo.log('onDecrementMinutes');
23891 this.time = this.time.add(Date.MINUTE, -1);
23895 onTogglePeriod: function()
23897 Roo.log('onTogglePeriod');
23898 this.time = this.time.add(Date.HOUR, 12);
23906 Roo.apply(Roo.bootstrap.TimeField, {
23910 cls: 'datepicker dropdown-menu',
23914 cls: 'datepicker-time',
23918 cls: 'table-condensed',
23947 cls: 'btn btn-info ok',
23975 * @class Roo.bootstrap.MonthField
23976 * @extends Roo.bootstrap.Input
23977 * Bootstrap MonthField class
23979 * @cfg {String} language default en
23982 * Create a new MonthField
23983 * @param {Object} config The config object
23986 Roo.bootstrap.MonthField = function(config){
23987 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23992 * Fires when this field show.
23993 * @param {Roo.bootstrap.MonthField} this
23994 * @param {Mixed} date The date value
23999 * Fires when this field hide.
24000 * @param {Roo.bootstrap.MonthField} this
24001 * @param {Mixed} date The date value
24006 * Fires when select a date.
24007 * @param {Roo.bootstrap.MonthField} this
24008 * @param {String} oldvalue The old value
24009 * @param {String} newvalue The new value
24015 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
24017 onRender: function(ct, position)
24020 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24022 this.language = this.language || 'en';
24023 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24024 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24026 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24027 this.isInline = false;
24028 this.isInput = true;
24029 this.component = this.el.select('.add-on', true).first() || false;
24030 this.component = (this.component && this.component.length === 0) ? false : this.component;
24031 this.hasInput = this.component && this.inputEL().length;
24033 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24035 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24037 this.picker().on('mousedown', this.onMousedown, this);
24038 this.picker().on('click', this.onClick, this);
24040 this.picker().addClass('datepicker-dropdown');
24042 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24043 v.setStyle('width', '189px');
24050 if(this.isInline) {
24056 setValue: function(v, suppressEvent)
24058 var o = this.getValue();
24060 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24064 if(suppressEvent !== true){
24065 this.fireEvent('select', this, o, v);
24070 getValue: function()
24075 onClick: function(e)
24077 e.stopPropagation();
24078 e.preventDefault();
24080 var target = e.getTarget();
24082 if(target.nodeName.toLowerCase() === 'i'){
24083 target = Roo.get(target).dom.parentNode;
24086 var nodeName = target.nodeName;
24087 var className = target.className;
24088 var html = target.innerHTML;
24090 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24094 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24096 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24102 picker : function()
24104 return this.pickerEl;
24107 fillMonths: function()
24110 var months = this.picker().select('>.datepicker-months td', true).first();
24112 months.dom.innerHTML = '';
24118 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24121 months.createChild(month);
24130 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24131 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24134 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24135 e.removeClass('active');
24137 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24138 e.addClass('active');
24145 if(this.isInline) {
24149 this.picker().removeClass(['bottom', 'top']);
24151 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24153 * place to the top of element!
24157 this.picker().addClass('top');
24158 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24163 this.picker().addClass('bottom');
24165 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24168 onFocus : function()
24170 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24174 onBlur : function()
24176 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24178 var d = this.inputEl().getValue();
24187 this.picker().show();
24188 this.picker().select('>.datepicker-months', true).first().show();
24192 this.fireEvent('show', this, this.date);
24197 if(this.isInline) {
24200 this.picker().hide();
24201 this.fireEvent('hide', this, this.date);
24205 onMousedown: function(e)
24207 e.stopPropagation();
24208 e.preventDefault();
24213 Roo.bootstrap.MonthField.superclass.keyup.call(this);
24217 fireKey: function(e)
24219 if (!this.picker().isVisible()){
24220 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24231 e.preventDefault();
24235 dir = e.keyCode == 37 ? -1 : 1;
24237 this.vIndex = this.vIndex + dir;
24239 if(this.vIndex < 0){
24243 if(this.vIndex > 11){
24247 if(isNaN(this.vIndex)){
24251 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24257 dir = e.keyCode == 38 ? -1 : 1;
24259 this.vIndex = this.vIndex + dir * 4;
24261 if(this.vIndex < 0){
24265 if(this.vIndex > 11){
24269 if(isNaN(this.vIndex)){
24273 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24278 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24279 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24283 e.preventDefault();
24286 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24287 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24303 this.picker().remove();
24308 Roo.apply(Roo.bootstrap.MonthField, {
24327 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24328 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24333 Roo.apply(Roo.bootstrap.MonthField, {
24337 cls: 'datepicker dropdown-menu roo-dynamic',
24341 cls: 'datepicker-months',
24345 cls: 'table-condensed',
24347 Roo.bootstrap.DateField.content
24367 * @class Roo.bootstrap.CheckBox
24368 * @extends Roo.bootstrap.Input
24369 * Bootstrap CheckBox class
24371 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24372 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24373 * @cfg {String} boxLabel The text that appears beside the checkbox
24374 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24375 * @cfg {Boolean} checked initnal the element
24376 * @cfg {Boolean} inline inline the element (default false)
24377 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24378 * @cfg {String} tooltip label tooltip
24381 * Create a new CheckBox
24382 * @param {Object} config The config object
24385 Roo.bootstrap.CheckBox = function(config){
24386 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24391 * Fires when the element is checked or unchecked.
24392 * @param {Roo.bootstrap.CheckBox} this This input
24393 * @param {Boolean} checked The new checked value
24398 * Fires when the element is click.
24399 * @param {Roo.bootstrap.CheckBox} this This input
24406 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24408 inputType: 'checkbox',
24417 // checkbox success does not make any sense really..
24422 getAutoCreate : function()
24424 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24430 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24433 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24439 type : this.inputType,
24440 value : this.inputValue,
24441 cls : 'roo-' + this.inputType, //'form-box',
24442 placeholder : this.placeholder || ''
24446 if(this.inputType != 'radio'){
24450 cls : 'roo-hidden-value',
24451 value : this.checked ? this.inputValue : this.valueOff
24456 if (this.weight) { // Validity check?
24457 cfg.cls += " " + this.inputType + "-" + this.weight;
24460 if (this.disabled) {
24461 input.disabled=true;
24465 input.checked = this.checked;
24470 input.name = this.name;
24472 if(this.inputType != 'radio'){
24473 hidden.name = this.name;
24474 input.name = '_hidden_' + this.name;
24479 input.cls += ' input-' + this.size;
24484 ['xs','sm','md','lg'].map(function(size){
24485 if (settings[size]) {
24486 cfg.cls += ' col-' + size + '-' + settings[size];
24490 var inputblock = input;
24492 if (this.before || this.after) {
24495 cls : 'input-group',
24500 inputblock.cn.push({
24502 cls : 'input-group-addon',
24507 inputblock.cn.push(input);
24509 if(this.inputType != 'radio'){
24510 inputblock.cn.push(hidden);
24514 inputblock.cn.push({
24516 cls : 'input-group-addon',
24522 var boxLabelCfg = false;
24528 //'for': id, // box label is handled by onclick - so no for...
24530 html: this.boxLabel
24533 boxLabelCfg.tooltip = this.tooltip;
24539 if (align ==='left' && this.fieldLabel.length) {
24540 // Roo.log("left and has label");
24545 cls : 'control-label',
24546 html : this.fieldLabel
24557 cfg.cn[1].cn.push(boxLabelCfg);
24560 if(this.labelWidth > 12){
24561 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24564 if(this.labelWidth < 13 && this.labelmd == 0){
24565 this.labelmd = this.labelWidth;
24568 if(this.labellg > 0){
24569 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24570 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24573 if(this.labelmd > 0){
24574 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24575 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24578 if(this.labelsm > 0){
24579 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24580 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24583 if(this.labelxs > 0){
24584 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24585 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24588 } else if ( this.fieldLabel.length) {
24589 // Roo.log(" label");
24593 tag: this.boxLabel ? 'span' : 'label',
24595 cls: 'control-label box-input-label',
24596 //cls : 'input-group-addon',
24597 html : this.fieldLabel
24604 cfg.cn.push(boxLabelCfg);
24609 // Roo.log(" no label && no align");
24610 cfg.cn = [ inputblock ] ;
24612 cfg.cn.push(boxLabelCfg);
24620 if(this.inputType != 'radio'){
24621 cfg.cn.push(hidden);
24629 * return the real input element.
24631 inputEl: function ()
24633 return this.el.select('input.roo-' + this.inputType,true).first();
24635 hiddenEl: function ()
24637 return this.el.select('input.roo-hidden-value',true).first();
24640 labelEl: function()
24642 return this.el.select('label.control-label',true).first();
24644 /* depricated... */
24648 return this.labelEl();
24651 boxLabelEl: function()
24653 return this.el.select('label.box-label',true).first();
24656 initEvents : function()
24658 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24660 this.inputEl().on('click', this.onClick, this);
24662 if (this.boxLabel) {
24663 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24666 this.startValue = this.getValue();
24669 Roo.bootstrap.CheckBox.register(this);
24673 onClick : function(e)
24675 if(this.fireEvent('click', this, e) !== false){
24676 this.setChecked(!this.checked);
24681 setChecked : function(state,suppressEvent)
24683 this.startValue = this.getValue();
24685 if(this.inputType == 'radio'){
24687 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24688 e.dom.checked = false;
24691 this.inputEl().dom.checked = true;
24693 this.inputEl().dom.value = this.inputValue;
24695 if(suppressEvent !== true){
24696 this.fireEvent('check', this, true);
24704 this.checked = state;
24706 this.inputEl().dom.checked = state;
24709 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24711 if(suppressEvent !== true){
24712 this.fireEvent('check', this, state);
24718 getValue : function()
24720 if(this.inputType == 'radio'){
24721 return this.getGroupValue();
24724 return this.hiddenEl().dom.value;
24728 getGroupValue : function()
24730 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24734 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24737 setValue : function(v,suppressEvent)
24739 if(this.inputType == 'radio'){
24740 this.setGroupValue(v, suppressEvent);
24744 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24749 setGroupValue : function(v, suppressEvent)
24751 this.startValue = this.getValue();
24753 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24754 e.dom.checked = false;
24756 if(e.dom.value == v){
24757 e.dom.checked = true;
24761 if(suppressEvent !== true){
24762 this.fireEvent('check', this, true);
24770 validate : function()
24772 if(this.getVisibilityEl().hasClass('hidden')){
24778 (this.inputType == 'radio' && this.validateRadio()) ||
24779 (this.inputType == 'checkbox' && this.validateCheckbox())
24785 this.markInvalid();
24789 validateRadio : function()
24791 if(this.getVisibilityEl().hasClass('hidden')){
24795 if(this.allowBlank){
24801 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24802 if(!e.dom.checked){
24814 validateCheckbox : function()
24817 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24818 //return (this.getValue() == this.inputValue) ? true : false;
24821 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24829 for(var i in group){
24830 if(group[i].el.isVisible(true)){
24838 for(var i in group){
24843 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24850 * Mark this field as valid
24852 markValid : function()
24856 this.fireEvent('valid', this);
24858 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24861 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24868 if(this.inputType == 'radio'){
24869 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24870 var fg = e.findParent('.form-group', false, true);
24871 if (Roo.bootstrap.version == 3) {
24872 fg.removeClass([_this.invalidClass, _this.validClass]);
24873 fg.addClass(_this.validClass);
24875 fg.removeClass(['is-valid', 'is-invalid']);
24876 fg.addClass('is-valid');
24884 var fg = this.el.findParent('.form-group', false, true);
24885 if (Roo.bootstrap.version == 3) {
24886 fg.removeClass([this.invalidClass, this.validClass]);
24887 fg.addClass(this.validClass);
24889 fg.removeClass(['is-valid', 'is-invalid']);
24890 fg.addClass('is-valid');
24895 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24901 for(var i in group){
24902 var fg = group[i].el.findParent('.form-group', false, true);
24903 if (Roo.bootstrap.version == 3) {
24904 fg.removeClass([this.invalidClass, this.validClass]);
24905 fg.addClass(this.validClass);
24907 fg.removeClass(['is-valid', 'is-invalid']);
24908 fg.addClass('is-valid');
24914 * Mark this field as invalid
24915 * @param {String} msg The validation message
24917 markInvalid : function(msg)
24919 if(this.allowBlank){
24925 this.fireEvent('invalid', this, msg);
24927 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24930 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24934 label.markInvalid();
24937 if(this.inputType == 'radio'){
24939 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24940 var fg = e.findParent('.form-group', false, true);
24941 if (Roo.bootstrap.version == 3) {
24942 fg.removeClass([_this.invalidClass, _this.validClass]);
24943 fg.addClass(_this.invalidClass);
24945 fg.removeClass(['is-invalid', 'is-valid']);
24946 fg.addClass('is-invalid');
24954 var fg = this.el.findParent('.form-group', false, true);
24955 if (Roo.bootstrap.version == 3) {
24956 fg.removeClass([_this.invalidClass, _this.validClass]);
24957 fg.addClass(_this.invalidClass);
24959 fg.removeClass(['is-invalid', 'is-valid']);
24960 fg.addClass('is-invalid');
24965 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24971 for(var i in group){
24972 var fg = group[i].el.findParent('.form-group', false, true);
24973 if (Roo.bootstrap.version == 3) {
24974 fg.removeClass([_this.invalidClass, _this.validClass]);
24975 fg.addClass(_this.invalidClass);
24977 fg.removeClass(['is-invalid', 'is-valid']);
24978 fg.addClass('is-invalid');
24984 clearInvalid : function()
24986 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24988 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24990 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24992 if (label && label.iconEl) {
24993 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24994 label.iconEl.removeClass(['is-invalid', 'is-valid']);
24998 disable : function()
25000 if(this.inputType != 'radio'){
25001 Roo.bootstrap.CheckBox.superclass.disable.call(this);
25008 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25009 _this.getActionEl().addClass(this.disabledClass);
25010 e.dom.disabled = true;
25014 this.disabled = true;
25015 this.fireEvent("disable", this);
25019 enable : function()
25021 if(this.inputType != 'radio'){
25022 Roo.bootstrap.CheckBox.superclass.enable.call(this);
25029 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25030 _this.getActionEl().removeClass(this.disabledClass);
25031 e.dom.disabled = false;
25035 this.disabled = false;
25036 this.fireEvent("enable", this);
25040 setBoxLabel : function(v)
25045 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25051 Roo.apply(Roo.bootstrap.CheckBox, {
25056 * register a CheckBox Group
25057 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25059 register : function(checkbox)
25061 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25062 this.groups[checkbox.groupId] = {};
25065 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25069 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25073 * fetch a CheckBox Group based on the group ID
25074 * @param {string} the group ID
25075 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25077 get: function(groupId) {
25078 if (typeof(this.groups[groupId]) == 'undefined') {
25082 return this.groups[groupId] ;
25095 * @class Roo.bootstrap.Radio
25096 * @extends Roo.bootstrap.Component
25097 * Bootstrap Radio class
25098 * @cfg {String} boxLabel - the label associated
25099 * @cfg {String} value - the value of radio
25102 * Create a new Radio
25103 * @param {Object} config The config object
25105 Roo.bootstrap.Radio = function(config){
25106 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25110 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25116 getAutoCreate : function()
25120 cls : 'form-group radio',
25125 html : this.boxLabel
25133 initEvents : function()
25135 this.parent().register(this);
25137 this.el.on('click', this.onClick, this);
25141 onClick : function(e)
25143 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25144 this.setChecked(true);
25148 setChecked : function(state, suppressEvent)
25150 this.parent().setValue(this.value, suppressEvent);
25154 setBoxLabel : function(v)
25159 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25174 * @class Roo.bootstrap.SecurePass
25175 * @extends Roo.bootstrap.Input
25176 * Bootstrap SecurePass class
25180 * Create a new SecurePass
25181 * @param {Object} config The config object
25184 Roo.bootstrap.SecurePass = function (config) {
25185 // these go here, so the translation tool can replace them..
25187 PwdEmpty: "Please type a password, and then retype it to confirm.",
25188 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25189 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25190 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25191 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25192 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25193 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25194 TooWeak: "Your password is Too Weak."
25196 this.meterLabel = "Password strength:";
25197 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25198 this.meterClass = [
25199 "roo-password-meter-tooweak",
25200 "roo-password-meter-weak",
25201 "roo-password-meter-medium",
25202 "roo-password-meter-strong",
25203 "roo-password-meter-grey"
25208 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25211 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25213 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25215 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25216 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25217 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25218 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25219 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25220 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25221 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25231 * @cfg {String/Object} Label for the strength meter (defaults to
25232 * 'Password strength:')
25237 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25238 * ['Weak', 'Medium', 'Strong'])
25241 pwdStrengths: false,
25254 initEvents: function ()
25256 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25258 if (this.el.is('input[type=password]') && Roo.isSafari) {
25259 this.el.on('keydown', this.SafariOnKeyDown, this);
25262 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25265 onRender: function (ct, position)
25267 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25268 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25269 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25271 this.trigger.createChild({
25276 cls: 'roo-password-meter-grey col-xs-12',
25279 //width: this.meterWidth + 'px'
25283 cls: 'roo-password-meter-text'
25289 if (this.hideTrigger) {
25290 this.trigger.setDisplayed(false);
25292 this.setSize(this.width || '', this.height || '');
25295 onDestroy: function ()
25297 if (this.trigger) {
25298 this.trigger.removeAllListeners();
25299 this.trigger.remove();
25302 this.wrap.remove();
25304 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25307 checkStrength: function ()
25309 var pwd = this.inputEl().getValue();
25310 if (pwd == this._lastPwd) {
25315 if (this.ClientSideStrongPassword(pwd)) {
25317 } else if (this.ClientSideMediumPassword(pwd)) {
25319 } else if (this.ClientSideWeakPassword(pwd)) {
25325 Roo.log('strength1: ' + strength);
25327 //var pm = this.trigger.child('div/div/div').dom;
25328 var pm = this.trigger.child('div/div');
25329 pm.removeClass(this.meterClass);
25330 pm.addClass(this.meterClass[strength]);
25333 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25335 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25337 this._lastPwd = pwd;
25341 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25343 this._lastPwd = '';
25345 var pm = this.trigger.child('div/div');
25346 pm.removeClass(this.meterClass);
25347 pm.addClass('roo-password-meter-grey');
25350 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25353 this.inputEl().dom.type='password';
25356 validateValue: function (value)
25358 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25361 if (value.length == 0) {
25362 if (this.allowBlank) {
25363 this.clearInvalid();
25367 this.markInvalid(this.errors.PwdEmpty);
25368 this.errorMsg = this.errors.PwdEmpty;
25376 if (!value.match(/[\x21-\x7e]+/)) {
25377 this.markInvalid(this.errors.PwdBadChar);
25378 this.errorMsg = this.errors.PwdBadChar;
25381 if (value.length < 6) {
25382 this.markInvalid(this.errors.PwdShort);
25383 this.errorMsg = this.errors.PwdShort;
25386 if (value.length > 16) {
25387 this.markInvalid(this.errors.PwdLong);
25388 this.errorMsg = this.errors.PwdLong;
25392 if (this.ClientSideStrongPassword(value)) {
25394 } else if (this.ClientSideMediumPassword(value)) {
25396 } else if (this.ClientSideWeakPassword(value)) {
25403 if (strength < 2) {
25404 //this.markInvalid(this.errors.TooWeak);
25405 this.errorMsg = this.errors.TooWeak;
25410 console.log('strength2: ' + strength);
25412 //var pm = this.trigger.child('div/div/div').dom;
25414 var pm = this.trigger.child('div/div');
25415 pm.removeClass(this.meterClass);
25416 pm.addClass(this.meterClass[strength]);
25418 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25420 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25422 this.errorMsg = '';
25426 CharacterSetChecks: function (type)
25429 this.fResult = false;
25432 isctype: function (character, type)
25435 case this.kCapitalLetter:
25436 if (character >= 'A' && character <= 'Z') {
25441 case this.kSmallLetter:
25442 if (character >= 'a' && character <= 'z') {
25448 if (character >= '0' && character <= '9') {
25453 case this.kPunctuation:
25454 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25465 IsLongEnough: function (pwd, size)
25467 return !(pwd == null || isNaN(size) || pwd.length < size);
25470 SpansEnoughCharacterSets: function (word, nb)
25472 if (!this.IsLongEnough(word, nb))
25477 var characterSetChecks = new Array(
25478 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25479 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25482 for (var index = 0; index < word.length; ++index) {
25483 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25484 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25485 characterSetChecks[nCharSet].fResult = true;
25492 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25493 if (characterSetChecks[nCharSet].fResult) {
25498 if (nCharSets < nb) {
25504 ClientSideStrongPassword: function (pwd)
25506 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25509 ClientSideMediumPassword: function (pwd)
25511 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25514 ClientSideWeakPassword: function (pwd)
25516 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25519 })//<script type="text/javascript">
25522 * Based Ext JS Library 1.1.1
25523 * Copyright(c) 2006-2007, Ext JS, LLC.
25529 * @class Roo.HtmlEditorCore
25530 * @extends Roo.Component
25531 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25533 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25536 Roo.HtmlEditorCore = function(config){
25539 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25544 * @event initialize
25545 * Fires when the editor is fully initialized (including the iframe)
25546 * @param {Roo.HtmlEditorCore} this
25551 * Fires when the editor is first receives the focus. Any insertion must wait
25552 * until after this event.
25553 * @param {Roo.HtmlEditorCore} this
25557 * @event beforesync
25558 * Fires before the textarea is updated with content from the editor iframe. Return false
25559 * to cancel the sync.
25560 * @param {Roo.HtmlEditorCore} this
25561 * @param {String} html
25565 * @event beforepush
25566 * Fires before the iframe editor is updated with content from the textarea. Return false
25567 * to cancel the push.
25568 * @param {Roo.HtmlEditorCore} this
25569 * @param {String} html
25574 * Fires when the textarea is updated with content from the editor iframe.
25575 * @param {Roo.HtmlEditorCore} this
25576 * @param {String} html
25581 * Fires when the iframe editor is updated with content from the textarea.
25582 * @param {Roo.HtmlEditorCore} this
25583 * @param {String} html
25588 * @event editorevent
25589 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25590 * @param {Roo.HtmlEditorCore} this
25596 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25598 // defaults : white / black...
25599 this.applyBlacklists();
25606 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25610 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25616 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25621 * @cfg {Number} height (in pixels)
25625 * @cfg {Number} width (in pixels)
25630 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25633 stylesheets: false,
25636 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25638 allowComments: false,
25642 // private properties
25643 validationEvent : false,
25645 initialized : false,
25647 sourceEditMode : false,
25648 onFocus : Roo.emptyFn,
25650 hideMode:'offsets',
25654 // blacklist + whitelisted elements..
25661 * Protected method that will not generally be called directly. It
25662 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25663 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25665 getDocMarkup : function(){
25669 // inherit styels from page...??
25670 if (this.stylesheets === false) {
25672 Roo.get(document.head).select('style').each(function(node) {
25673 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25676 Roo.get(document.head).select('link').each(function(node) {
25677 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25680 } else if (!this.stylesheets.length) {
25682 st = '<style type="text/css">' +
25683 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25686 for (var i in this.stylesheets) {
25687 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25692 st += '<style type="text/css">' +
25693 'IMG { cursor: pointer } ' +
25696 var cls = 'roo-htmleditor-body';
25698 if(this.bodyCls.length){
25699 cls += ' ' + this.bodyCls;
25702 return '<html><head>' + st +
25703 //<style type="text/css">' +
25704 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25706 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25710 onRender : function(ct, position)
25713 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25714 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25717 this.el.dom.style.border = '0 none';
25718 this.el.dom.setAttribute('tabIndex', -1);
25719 this.el.addClass('x-hidden hide');
25723 if(Roo.isIE){ // fix IE 1px bogus margin
25724 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25728 this.frameId = Roo.id();
25732 var iframe = this.owner.wrap.createChild({
25734 cls: 'form-control', // bootstrap..
25736 name: this.frameId,
25737 frameBorder : 'no',
25738 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25743 this.iframe = iframe.dom;
25745 this.assignDocWin();
25747 this.doc.designMode = 'on';
25750 this.doc.write(this.getDocMarkup());
25754 var task = { // must defer to wait for browser to be ready
25756 //console.log("run task?" + this.doc.readyState);
25757 this.assignDocWin();
25758 if(this.doc.body || this.doc.readyState == 'complete'){
25760 this.doc.designMode="on";
25764 Roo.TaskMgr.stop(task);
25765 this.initEditor.defer(10, this);
25772 Roo.TaskMgr.start(task);
25777 onResize : function(w, h)
25779 Roo.log('resize: ' +w + ',' + h );
25780 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25784 if(typeof w == 'number'){
25786 this.iframe.style.width = w + 'px';
25788 if(typeof h == 'number'){
25790 this.iframe.style.height = h + 'px';
25792 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25799 * Toggles the editor between standard and source edit mode.
25800 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25802 toggleSourceEdit : function(sourceEditMode){
25804 this.sourceEditMode = sourceEditMode === true;
25806 if(this.sourceEditMode){
25808 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25811 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25812 //this.iframe.className = '';
25815 //this.setSize(this.owner.wrap.getSize());
25816 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25823 * Protected method that will not generally be called directly. If you need/want
25824 * custom HTML cleanup, this is the method you should override.
25825 * @param {String} html The HTML to be cleaned
25826 * return {String} The cleaned HTML
25828 cleanHtml : function(html){
25829 html = String(html);
25830 if(html.length > 5){
25831 if(Roo.isSafari){ // strip safari nonsense
25832 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25835 if(html == ' '){
25842 * HTML Editor -> Textarea
25843 * Protected method that will not generally be called directly. Syncs the contents
25844 * of the editor iframe with the textarea.
25846 syncValue : function(){
25847 if(this.initialized){
25848 var bd = (this.doc.body || this.doc.documentElement);
25849 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25850 var html = bd.innerHTML;
25852 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25853 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25855 html = '<div style="'+m[0]+'">' + html + '</div>';
25858 html = this.cleanHtml(html);
25859 // fix up the special chars.. normaly like back quotes in word...
25860 // however we do not want to do this with chinese..
25861 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25863 var cc = match.charCodeAt();
25865 // Get the character value, handling surrogate pairs
25866 if (match.length == 2) {
25867 // It's a surrogate pair, calculate the Unicode code point
25868 var high = match.charCodeAt(0) - 0xD800;
25869 var low = match.charCodeAt(1) - 0xDC00;
25870 cc = (high * 0x400) + low + 0x10000;
25872 (cc >= 0x4E00 && cc < 0xA000 ) ||
25873 (cc >= 0x3400 && cc < 0x4E00 ) ||
25874 (cc >= 0xf900 && cc < 0xfb00 )
25879 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25880 return "&#" + cc + ";";
25887 if(this.owner.fireEvent('beforesync', this, html) !== false){
25888 this.el.dom.value = html;
25889 this.owner.fireEvent('sync', this, html);
25895 * Protected method that will not generally be called directly. Pushes the value of the textarea
25896 * into the iframe editor.
25898 pushValue : function(){
25899 if(this.initialized){
25900 var v = this.el.dom.value.trim();
25902 // if(v.length < 1){
25906 if(this.owner.fireEvent('beforepush', this, v) !== false){
25907 var d = (this.doc.body || this.doc.documentElement);
25909 this.cleanUpPaste();
25910 this.el.dom.value = d.innerHTML;
25911 this.owner.fireEvent('push', this, v);
25917 deferFocus : function(){
25918 this.focus.defer(10, this);
25922 focus : function(){
25923 if(this.win && !this.sourceEditMode){
25930 assignDocWin: function()
25932 var iframe = this.iframe;
25935 this.doc = iframe.contentWindow.document;
25936 this.win = iframe.contentWindow;
25938 // if (!Roo.get(this.frameId)) {
25941 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25942 // this.win = Roo.get(this.frameId).dom.contentWindow;
25944 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25948 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25949 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25954 initEditor : function(){
25955 //console.log("INIT EDITOR");
25956 this.assignDocWin();
25960 this.doc.designMode="on";
25962 this.doc.write(this.getDocMarkup());
25965 var dbody = (this.doc.body || this.doc.documentElement);
25966 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25967 // this copies styles from the containing element into thsi one..
25968 // not sure why we need all of this..
25969 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25971 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25972 //ss['background-attachment'] = 'fixed'; // w3c
25973 dbody.bgProperties = 'fixed'; // ie
25974 //Roo.DomHelper.applyStyles(dbody, ss);
25975 Roo.EventManager.on(this.doc, {
25976 //'mousedown': this.onEditorEvent,
25977 'mouseup': this.onEditorEvent,
25978 'dblclick': this.onEditorEvent,
25979 'click': this.onEditorEvent,
25980 'keyup': this.onEditorEvent,
25985 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25987 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25988 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25990 this.initialized = true;
25992 this.owner.fireEvent('initialize', this);
25997 onDestroy : function(){
26003 //for (var i =0; i < this.toolbars.length;i++) {
26004 // // fixme - ask toolbars for heights?
26005 // this.toolbars[i].onDestroy();
26008 //this.wrap.dom.innerHTML = '';
26009 //this.wrap.remove();
26014 onFirstFocus : function(){
26016 this.assignDocWin();
26019 this.activated = true;
26022 if(Roo.isGecko){ // prevent silly gecko errors
26024 var s = this.win.getSelection();
26025 if(!s.focusNode || s.focusNode.nodeType != 3){
26026 var r = s.getRangeAt(0);
26027 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26032 this.execCmd('useCSS', true);
26033 this.execCmd('styleWithCSS', false);
26036 this.owner.fireEvent('activate', this);
26040 adjustFont: function(btn){
26041 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26042 //if(Roo.isSafari){ // safari
26045 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26046 if(Roo.isSafari){ // safari
26047 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26048 v = (v < 10) ? 10 : v;
26049 v = (v > 48) ? 48 : v;
26050 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26055 v = Math.max(1, v+adjust);
26057 this.execCmd('FontSize', v );
26060 onEditorEvent : function(e)
26062 this.owner.fireEvent('editorevent', this, e);
26063 // this.updateToolbar();
26064 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26067 insertTag : function(tg)
26069 // could be a bit smarter... -> wrap the current selected tRoo..
26070 if (tg.toLowerCase() == 'span' ||
26071 tg.toLowerCase() == 'code' ||
26072 tg.toLowerCase() == 'sup' ||
26073 tg.toLowerCase() == 'sub'
26076 range = this.createRange(this.getSelection());
26077 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26078 wrappingNode.appendChild(range.extractContents());
26079 range.insertNode(wrappingNode);
26086 this.execCmd("formatblock", tg);
26090 insertText : function(txt)
26094 var range = this.createRange();
26095 range.deleteContents();
26096 //alert(Sender.getAttribute('label'));
26098 range.insertNode(this.doc.createTextNode(txt));
26104 * Executes a Midas editor command on the editor document and performs necessary focus and
26105 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26106 * @param {String} cmd The Midas command
26107 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26109 relayCmd : function(cmd, value){
26111 this.execCmd(cmd, value);
26112 this.owner.fireEvent('editorevent', this);
26113 //this.updateToolbar();
26114 this.owner.deferFocus();
26118 * Executes a Midas editor command directly on the editor document.
26119 * For visual commands, you should use {@link #relayCmd} instead.
26120 * <b>This should only be called after the editor is initialized.</b>
26121 * @param {String} cmd The Midas command
26122 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26124 execCmd : function(cmd, value){
26125 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26132 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26134 * @param {String} text | dom node..
26136 insertAtCursor : function(text)
26139 if(!this.activated){
26145 var r = this.doc.selection.createRange();
26156 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26160 // from jquery ui (MIT licenced)
26162 var win = this.win;
26164 if (win.getSelection && win.getSelection().getRangeAt) {
26165 range = win.getSelection().getRangeAt(0);
26166 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26167 range.insertNode(node);
26168 } else if (win.document.selection && win.document.selection.createRange) {
26169 // no firefox support
26170 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26171 win.document.selection.createRange().pasteHTML(txt);
26173 // no firefox support
26174 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26175 this.execCmd('InsertHTML', txt);
26184 mozKeyPress : function(e){
26186 var c = e.getCharCode(), cmd;
26189 c = String.fromCharCode(c).toLowerCase();
26203 this.cleanUpPaste.defer(100, this);
26211 e.preventDefault();
26219 fixKeys : function(){ // load time branching for fastest keydown performance
26221 return function(e){
26222 var k = e.getKey(), r;
26225 r = this.doc.selection.createRange();
26228 r.pasteHTML('    ');
26235 r = this.doc.selection.createRange();
26237 var target = r.parentElement();
26238 if(!target || target.tagName.toLowerCase() != 'li'){
26240 r.pasteHTML('<br />');
26246 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26247 this.cleanUpPaste.defer(100, this);
26253 }else if(Roo.isOpera){
26254 return function(e){
26255 var k = e.getKey();
26259 this.execCmd('InsertHTML','    ');
26262 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26263 this.cleanUpPaste.defer(100, this);
26268 }else if(Roo.isSafari){
26269 return function(e){
26270 var k = e.getKey();
26274 this.execCmd('InsertText','\t');
26278 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26279 this.cleanUpPaste.defer(100, this);
26287 getAllAncestors: function()
26289 var p = this.getSelectedNode();
26292 a.push(p); // push blank onto stack..
26293 p = this.getParentElement();
26297 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26301 a.push(this.doc.body);
26305 lastSelNode : false,
26308 getSelection : function()
26310 this.assignDocWin();
26311 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26314 getSelectedNode: function()
26316 // this may only work on Gecko!!!
26318 // should we cache this!!!!
26323 var range = this.createRange(this.getSelection()).cloneRange();
26326 var parent = range.parentElement();
26328 var testRange = range.duplicate();
26329 testRange.moveToElementText(parent);
26330 if (testRange.inRange(range)) {
26333 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26336 parent = parent.parentElement;
26341 // is ancestor a text element.
26342 var ac = range.commonAncestorContainer;
26343 if (ac.nodeType == 3) {
26344 ac = ac.parentNode;
26347 var ar = ac.childNodes;
26350 var other_nodes = [];
26351 var has_other_nodes = false;
26352 for (var i=0;i<ar.length;i++) {
26353 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26356 // fullly contained node.
26358 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26363 // probably selected..
26364 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26365 other_nodes.push(ar[i]);
26369 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26374 has_other_nodes = true;
26376 if (!nodes.length && other_nodes.length) {
26377 nodes= other_nodes;
26379 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26385 createRange: function(sel)
26387 // this has strange effects when using with
26388 // top toolbar - not sure if it's a great idea.
26389 //this.editor.contentWindow.focus();
26390 if (typeof sel != "undefined") {
26392 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26394 return this.doc.createRange();
26397 return this.doc.createRange();
26400 getParentElement: function()
26403 this.assignDocWin();
26404 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26406 var range = this.createRange(sel);
26409 var p = range.commonAncestorContainer;
26410 while (p.nodeType == 3) { // text node
26421 * Range intersection.. the hard stuff...
26425 * [ -- selected range --- ]
26429 * if end is before start or hits it. fail.
26430 * if start is after end or hits it fail.
26432 * if either hits (but other is outside. - then it's not
26438 // @see http://www.thismuchiknow.co.uk/?p=64.
26439 rangeIntersectsNode : function(range, node)
26441 var nodeRange = node.ownerDocument.createRange();
26443 nodeRange.selectNode(node);
26445 nodeRange.selectNodeContents(node);
26448 var rangeStartRange = range.cloneRange();
26449 rangeStartRange.collapse(true);
26451 var rangeEndRange = range.cloneRange();
26452 rangeEndRange.collapse(false);
26454 var nodeStartRange = nodeRange.cloneRange();
26455 nodeStartRange.collapse(true);
26457 var nodeEndRange = nodeRange.cloneRange();
26458 nodeEndRange.collapse(false);
26460 return rangeStartRange.compareBoundaryPoints(
26461 Range.START_TO_START, nodeEndRange) == -1 &&
26462 rangeEndRange.compareBoundaryPoints(
26463 Range.START_TO_START, nodeStartRange) == 1;
26467 rangeCompareNode : function(range, node)
26469 var nodeRange = node.ownerDocument.createRange();
26471 nodeRange.selectNode(node);
26473 nodeRange.selectNodeContents(node);
26477 range.collapse(true);
26479 nodeRange.collapse(true);
26481 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26482 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26484 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26486 var nodeIsBefore = ss == 1;
26487 var nodeIsAfter = ee == -1;
26489 if (nodeIsBefore && nodeIsAfter) {
26492 if (!nodeIsBefore && nodeIsAfter) {
26493 return 1; //right trailed.
26496 if (nodeIsBefore && !nodeIsAfter) {
26497 return 2; // left trailed.
26503 // private? - in a new class?
26504 cleanUpPaste : function()
26506 // cleans up the whole document..
26507 Roo.log('cleanuppaste');
26509 this.cleanUpChildren(this.doc.body);
26510 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26511 if (clean != this.doc.body.innerHTML) {
26512 this.doc.body.innerHTML = clean;
26517 cleanWordChars : function(input) {// change the chars to hex code
26518 var he = Roo.HtmlEditorCore;
26520 var output = input;
26521 Roo.each(he.swapCodes, function(sw) {
26522 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26524 output = output.replace(swapper, sw[1]);
26531 cleanUpChildren : function (n)
26533 if (!n.childNodes.length) {
26536 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26537 this.cleanUpChild(n.childNodes[i]);
26544 cleanUpChild : function (node)
26547 //console.log(node);
26548 if (node.nodeName == "#text") {
26549 // clean up silly Windows -- stuff?
26552 if (node.nodeName == "#comment") {
26553 if (!this.allowComments) {
26554 node.parentNode.removeChild(node);
26556 // clean up silly Windows -- stuff?
26559 var lcname = node.tagName.toLowerCase();
26560 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26561 // whitelist of tags..
26563 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26565 node.parentNode.removeChild(node);
26570 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26572 // spans with no attributes - just remove them..
26573 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26574 remove_keep_children = true;
26577 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26578 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26580 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26581 // remove_keep_children = true;
26584 if (remove_keep_children) {
26585 this.cleanUpChildren(node);
26586 // inserts everything just before this node...
26587 while (node.childNodes.length) {
26588 var cn = node.childNodes[0];
26589 node.removeChild(cn);
26590 node.parentNode.insertBefore(cn, node);
26592 node.parentNode.removeChild(node);
26596 if (!node.attributes || !node.attributes.length) {
26601 this.cleanUpChildren(node);
26605 function cleanAttr(n,v)
26608 if (v.match(/^\./) || v.match(/^\//)) {
26611 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26614 if (v.match(/^#/)) {
26617 if (v.match(/^\{/)) { // allow template editing.
26620 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26621 node.removeAttribute(n);
26625 var cwhite = this.cwhite;
26626 var cblack = this.cblack;
26628 function cleanStyle(n,v)
26630 if (v.match(/expression/)) { //XSS?? should we even bother..
26631 node.removeAttribute(n);
26635 var parts = v.split(/;/);
26638 Roo.each(parts, function(p) {
26639 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26643 var l = p.split(':').shift().replace(/\s+/g,'');
26644 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26646 if ( cwhite.length && cblack.indexOf(l) > -1) {
26647 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26648 //node.removeAttribute(n);
26652 // only allow 'c whitelisted system attributes'
26653 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26654 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26655 //node.removeAttribute(n);
26665 if (clean.length) {
26666 node.setAttribute(n, clean.join(';'));
26668 node.removeAttribute(n);
26674 for (var i = node.attributes.length-1; i > -1 ; i--) {
26675 var a = node.attributes[i];
26678 if (a.name.toLowerCase().substr(0,2)=='on') {
26679 node.removeAttribute(a.name);
26682 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26683 node.removeAttribute(a.name);
26686 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26687 cleanAttr(a.name,a.value); // fixme..
26690 if (a.name == 'style') {
26691 cleanStyle(a.name,a.value);
26694 /// clean up MS crap..
26695 // tecnically this should be a list of valid class'es..
26698 if (a.name == 'class') {
26699 if (a.value.match(/^Mso/)) {
26700 node.removeAttribute('class');
26703 if (a.value.match(/^body$/)) {
26704 node.removeAttribute('class');
26715 this.cleanUpChildren(node);
26721 * Clean up MS wordisms...
26723 cleanWord : function(node)
26726 this.cleanWord(this.doc.body);
26731 node.nodeName == 'SPAN' &&
26732 !node.hasAttributes() &&
26733 node.childNodes.length == 1 &&
26734 node.firstChild.nodeName == "#text"
26736 var textNode = node.firstChild;
26737 node.removeChild(textNode);
26738 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26739 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26741 node.parentNode.insertBefore(textNode, node);
26742 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26743 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26745 node.parentNode.removeChild(node);
26748 if (node.nodeName == "#text") {
26749 // clean up silly Windows -- stuff?
26752 if (node.nodeName == "#comment") {
26753 node.parentNode.removeChild(node);
26754 // clean up silly Windows -- stuff?
26758 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26759 node.parentNode.removeChild(node);
26762 //Roo.log(node.tagName);
26763 // remove - but keep children..
26764 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26765 //Roo.log('-- removed');
26766 while (node.childNodes.length) {
26767 var cn = node.childNodes[0];
26768 node.removeChild(cn);
26769 node.parentNode.insertBefore(cn, node);
26770 // move node to parent - and clean it..
26771 this.cleanWord(cn);
26773 node.parentNode.removeChild(node);
26774 /// no need to iterate chidlren = it's got none..
26775 //this.iterateChildren(node, this.cleanWord);
26779 if (node.className.length) {
26781 var cn = node.className.split(/\W+/);
26783 Roo.each(cn, function(cls) {
26784 if (cls.match(/Mso[a-zA-Z]+/)) {
26789 node.className = cna.length ? cna.join(' ') : '';
26791 node.removeAttribute("class");
26795 if (node.hasAttribute("lang")) {
26796 node.removeAttribute("lang");
26799 if (node.hasAttribute("style")) {
26801 var styles = node.getAttribute("style").split(";");
26803 Roo.each(styles, function(s) {
26804 if (!s.match(/:/)) {
26807 var kv = s.split(":");
26808 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26811 // what ever is left... we allow.
26814 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26815 if (!nstyle.length) {
26816 node.removeAttribute('style');
26819 this.iterateChildren(node, this.cleanWord);
26825 * iterateChildren of a Node, calling fn each time, using this as the scole..
26826 * @param {DomNode} node node to iterate children of.
26827 * @param {Function} fn method of this class to call on each item.
26829 iterateChildren : function(node, fn)
26831 if (!node.childNodes.length) {
26834 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26835 fn.call(this, node.childNodes[i])
26841 * cleanTableWidths.
26843 * Quite often pasting from word etc.. results in tables with column and widths.
26844 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26847 cleanTableWidths : function(node)
26852 this.cleanTableWidths(this.doc.body);
26857 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26860 Roo.log(node.tagName);
26861 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26862 this.iterateChildren(node, this.cleanTableWidths);
26865 if (node.hasAttribute('width')) {
26866 node.removeAttribute('width');
26870 if (node.hasAttribute("style")) {
26873 var styles = node.getAttribute("style").split(";");
26875 Roo.each(styles, function(s) {
26876 if (!s.match(/:/)) {
26879 var kv = s.split(":");
26880 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26883 // what ever is left... we allow.
26886 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26887 if (!nstyle.length) {
26888 node.removeAttribute('style');
26892 this.iterateChildren(node, this.cleanTableWidths);
26900 domToHTML : function(currentElement, depth, nopadtext) {
26902 depth = depth || 0;
26903 nopadtext = nopadtext || false;
26905 if (!currentElement) {
26906 return this.domToHTML(this.doc.body);
26909 //Roo.log(currentElement);
26911 var allText = false;
26912 var nodeName = currentElement.nodeName;
26913 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26915 if (nodeName == '#text') {
26917 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26922 if (nodeName != 'BODY') {
26925 // Prints the node tagName, such as <A>, <IMG>, etc
26928 for(i = 0; i < currentElement.attributes.length;i++) {
26930 var aname = currentElement.attributes.item(i).name;
26931 if (!currentElement.attributes.item(i).value.length) {
26934 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26937 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26946 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26949 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26954 // Traverse the tree
26956 var currentElementChild = currentElement.childNodes.item(i);
26957 var allText = true;
26958 var innerHTML = '';
26960 while (currentElementChild) {
26961 // Formatting code (indent the tree so it looks nice on the screen)
26962 var nopad = nopadtext;
26963 if (lastnode == 'SPAN') {
26967 if (currentElementChild.nodeName == '#text') {
26968 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26969 toadd = nopadtext ? toadd : toadd.trim();
26970 if (!nopad && toadd.length > 80) {
26971 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26973 innerHTML += toadd;
26976 currentElementChild = currentElement.childNodes.item(i);
26982 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26984 // Recursively traverse the tree structure of the child node
26985 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26986 lastnode = currentElementChild.nodeName;
26988 currentElementChild=currentElement.childNodes.item(i);
26994 // The remaining code is mostly for formatting the tree
26995 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
27000 ret+= "</"+tagName+">";
27006 applyBlacklists : function()
27008 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
27009 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
27013 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27014 if (b.indexOf(tag) > -1) {
27017 this.white.push(tag);
27021 Roo.each(w, function(tag) {
27022 if (b.indexOf(tag) > -1) {
27025 if (this.white.indexOf(tag) > -1) {
27028 this.white.push(tag);
27033 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27034 if (w.indexOf(tag) > -1) {
27037 this.black.push(tag);
27041 Roo.each(b, function(tag) {
27042 if (w.indexOf(tag) > -1) {
27045 if (this.black.indexOf(tag) > -1) {
27048 this.black.push(tag);
27053 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27054 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27058 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27059 if (b.indexOf(tag) > -1) {
27062 this.cwhite.push(tag);
27066 Roo.each(w, function(tag) {
27067 if (b.indexOf(tag) > -1) {
27070 if (this.cwhite.indexOf(tag) > -1) {
27073 this.cwhite.push(tag);
27078 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27079 if (w.indexOf(tag) > -1) {
27082 this.cblack.push(tag);
27086 Roo.each(b, function(tag) {
27087 if (w.indexOf(tag) > -1) {
27090 if (this.cblack.indexOf(tag) > -1) {
27093 this.cblack.push(tag);
27098 setStylesheets : function(stylesheets)
27100 if(typeof(stylesheets) == 'string'){
27101 Roo.get(this.iframe.contentDocument.head).createChild({
27103 rel : 'stylesheet',
27112 Roo.each(stylesheets, function(s) {
27117 Roo.get(_this.iframe.contentDocument.head).createChild({
27119 rel : 'stylesheet',
27128 removeStylesheets : function()
27132 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27137 setStyle : function(style)
27139 Roo.get(this.iframe.contentDocument.head).createChild({
27148 // hide stuff that is not compatible
27162 * @event specialkey
27166 * @cfg {String} fieldClass @hide
27169 * @cfg {String} focusClass @hide
27172 * @cfg {String} autoCreate @hide
27175 * @cfg {String} inputType @hide
27178 * @cfg {String} invalidClass @hide
27181 * @cfg {String} invalidText @hide
27184 * @cfg {String} msgFx @hide
27187 * @cfg {String} validateOnBlur @hide
27191 Roo.HtmlEditorCore.white = [
27192 'area', 'br', 'img', 'input', 'hr', 'wbr',
27194 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27195 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27196 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27197 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27198 'table', 'ul', 'xmp',
27200 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27203 'dir', 'menu', 'ol', 'ul', 'dl',
27209 Roo.HtmlEditorCore.black = [
27210 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27212 'base', 'basefont', 'bgsound', 'blink', 'body',
27213 'frame', 'frameset', 'head', 'html', 'ilayer',
27214 'iframe', 'layer', 'link', 'meta', 'object',
27215 'script', 'style' ,'title', 'xml' // clean later..
27217 Roo.HtmlEditorCore.clean = [
27218 'script', 'style', 'title', 'xml'
27220 Roo.HtmlEditorCore.remove = [
27225 Roo.HtmlEditorCore.ablack = [
27229 Roo.HtmlEditorCore.aclean = [
27230 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27234 Roo.HtmlEditorCore.pwhite= [
27235 'http', 'https', 'mailto'
27238 // white listed style attributes.
27239 Roo.HtmlEditorCore.cwhite= [
27240 // 'text-align', /// default is to allow most things..
27246 // black listed style attributes.
27247 Roo.HtmlEditorCore.cblack= [
27248 // 'font-size' -- this can be set by the project
27252 Roo.HtmlEditorCore.swapCodes =[
27253 [ 8211, "–" ],
27254 [ 8212, "—" ],
27271 * @class Roo.bootstrap.HtmlEditor
27272 * @extends Roo.bootstrap.TextArea
27273 * Bootstrap HtmlEditor class
27276 * Create a new HtmlEditor
27277 * @param {Object} config The config object
27280 Roo.bootstrap.HtmlEditor = function(config){
27281 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27282 if (!this.toolbars) {
27283 this.toolbars = [];
27286 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27289 * @event initialize
27290 * Fires when the editor is fully initialized (including the iframe)
27291 * @param {HtmlEditor} this
27296 * Fires when the editor is first receives the focus. Any insertion must wait
27297 * until after this event.
27298 * @param {HtmlEditor} this
27302 * @event beforesync
27303 * Fires before the textarea is updated with content from the editor iframe. Return false
27304 * to cancel the sync.
27305 * @param {HtmlEditor} this
27306 * @param {String} html
27310 * @event beforepush
27311 * Fires before the iframe editor is updated with content from the textarea. Return false
27312 * to cancel the push.
27313 * @param {HtmlEditor} this
27314 * @param {String} html
27319 * Fires when the textarea is updated with content from the editor iframe.
27320 * @param {HtmlEditor} this
27321 * @param {String} html
27326 * Fires when the iframe editor is updated with content from the textarea.
27327 * @param {HtmlEditor} this
27328 * @param {String} html
27332 * @event editmodechange
27333 * Fires when the editor switches edit modes
27334 * @param {HtmlEditor} this
27335 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27337 editmodechange: true,
27339 * @event editorevent
27340 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27341 * @param {HtmlEditor} this
27345 * @event firstfocus
27346 * Fires when on first focus - needed by toolbars..
27347 * @param {HtmlEditor} this
27352 * Auto save the htmlEditor value as a file into Events
27353 * @param {HtmlEditor} this
27357 * @event savedpreview
27358 * preview the saved version of htmlEditor
27359 * @param {HtmlEditor} this
27366 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27370 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27375 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27380 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27385 * @cfg {Number} height (in pixels)
27389 * @cfg {Number} width (in pixels)
27394 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27397 stylesheets: false,
27402 // private properties
27403 validationEvent : false,
27405 initialized : false,
27408 onFocus : Roo.emptyFn,
27410 hideMode:'offsets',
27412 tbContainer : false,
27416 toolbarContainer :function() {
27417 return this.wrap.select('.x-html-editor-tb',true).first();
27421 * Protected method that will not generally be called directly. It
27422 * is called when the editor creates its toolbar. Override this method if you need to
27423 * add custom toolbar buttons.
27424 * @param {HtmlEditor} editor
27426 createToolbar : function(){
27427 Roo.log('renewing');
27428 Roo.log("create toolbars");
27430 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27431 this.toolbars[0].render(this.toolbarContainer());
27435 // if (!editor.toolbars || !editor.toolbars.length) {
27436 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27439 // for (var i =0 ; i < editor.toolbars.length;i++) {
27440 // editor.toolbars[i] = Roo.factory(
27441 // typeof(editor.toolbars[i]) == 'string' ?
27442 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27443 // Roo.bootstrap.HtmlEditor);
27444 // editor.toolbars[i].init(editor);
27450 onRender : function(ct, position)
27452 // Roo.log("Call onRender: " + this.xtype);
27454 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27456 this.wrap = this.inputEl().wrap({
27457 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27460 this.editorcore.onRender(ct, position);
27462 if (this.resizable) {
27463 this.resizeEl = new Roo.Resizable(this.wrap, {
27467 minHeight : this.height,
27468 height: this.height,
27469 handles : this.resizable,
27472 resize : function(r, w, h) {
27473 _t.onResize(w,h); // -something
27479 this.createToolbar(this);
27482 if(!this.width && this.resizable){
27483 this.setSize(this.wrap.getSize());
27485 if (this.resizeEl) {
27486 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27487 // should trigger onReize..
27493 onResize : function(w, h)
27495 Roo.log('resize: ' +w + ',' + h );
27496 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27500 if(this.inputEl() ){
27501 if(typeof w == 'number'){
27502 var aw = w - this.wrap.getFrameWidth('lr');
27503 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27506 if(typeof h == 'number'){
27507 var tbh = -11; // fixme it needs to tool bar size!
27508 for (var i =0; i < this.toolbars.length;i++) {
27509 // fixme - ask toolbars for heights?
27510 tbh += this.toolbars[i].el.getHeight();
27511 //if (this.toolbars[i].footer) {
27512 // tbh += this.toolbars[i].footer.el.getHeight();
27520 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27521 ah -= 5; // knock a few pixes off for look..
27522 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27526 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27527 this.editorcore.onResize(ew,eh);
27532 * Toggles the editor between standard and source edit mode.
27533 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27535 toggleSourceEdit : function(sourceEditMode)
27537 this.editorcore.toggleSourceEdit(sourceEditMode);
27539 if(this.editorcore.sourceEditMode){
27540 Roo.log('editor - showing textarea');
27543 // Roo.log(this.syncValue());
27545 this.inputEl().removeClass(['hide', 'x-hidden']);
27546 this.inputEl().dom.removeAttribute('tabIndex');
27547 this.inputEl().focus();
27549 Roo.log('editor - hiding textarea');
27551 // Roo.log(this.pushValue());
27554 this.inputEl().addClass(['hide', 'x-hidden']);
27555 this.inputEl().dom.setAttribute('tabIndex', -1);
27556 //this.deferFocus();
27559 if(this.resizable){
27560 this.setSize(this.wrap.getSize());
27563 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27566 // private (for BoxComponent)
27567 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27569 // private (for BoxComponent)
27570 getResizeEl : function(){
27574 // private (for BoxComponent)
27575 getPositionEl : function(){
27580 initEvents : function(){
27581 this.originalValue = this.getValue();
27585 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27588 // markInvalid : Roo.emptyFn,
27590 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27593 // clearInvalid : Roo.emptyFn,
27595 setValue : function(v){
27596 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27597 this.editorcore.pushValue();
27602 deferFocus : function(){
27603 this.focus.defer(10, this);
27607 focus : function(){
27608 this.editorcore.focus();
27614 onDestroy : function(){
27620 for (var i =0; i < this.toolbars.length;i++) {
27621 // fixme - ask toolbars for heights?
27622 this.toolbars[i].onDestroy();
27625 this.wrap.dom.innerHTML = '';
27626 this.wrap.remove();
27631 onFirstFocus : function(){
27632 //Roo.log("onFirstFocus");
27633 this.editorcore.onFirstFocus();
27634 for (var i =0; i < this.toolbars.length;i++) {
27635 this.toolbars[i].onFirstFocus();
27641 syncValue : function()
27643 this.editorcore.syncValue();
27646 pushValue : function()
27648 this.editorcore.pushValue();
27652 // hide stuff that is not compatible
27666 * @event specialkey
27670 * @cfg {String} fieldClass @hide
27673 * @cfg {String} focusClass @hide
27676 * @cfg {String} autoCreate @hide
27679 * @cfg {String} inputType @hide
27683 * @cfg {String} invalidText @hide
27686 * @cfg {String} msgFx @hide
27689 * @cfg {String} validateOnBlur @hide
27698 Roo.namespace('Roo.bootstrap.htmleditor');
27700 * @class Roo.bootstrap.HtmlEditorToolbar1
27706 new Roo.bootstrap.HtmlEditor({
27709 new Roo.bootstrap.HtmlEditorToolbar1({
27710 disable : { fonts: 1 , format: 1, ..., ... , ...],
27716 * @cfg {Object} disable List of elements to disable..
27717 * @cfg {Array} btns List of additional buttons.
27721 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27724 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27727 Roo.apply(this, config);
27729 // default disabled, based on 'good practice'..
27730 this.disable = this.disable || {};
27731 Roo.applyIf(this.disable, {
27734 specialElements : true
27736 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27738 this.editor = config.editor;
27739 this.editorcore = config.editor.editorcore;
27741 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27743 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27744 // dont call parent... till later.
27746 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27751 editorcore : false,
27756 "h1","h2","h3","h4","h5","h6",
27758 "abbr", "acronym", "address", "cite", "samp", "var",
27762 onRender : function(ct, position)
27764 // Roo.log("Call onRender: " + this.xtype);
27766 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27768 this.el.dom.style.marginBottom = '0';
27770 var editorcore = this.editorcore;
27771 var editor= this.editor;
27774 var btn = function(id,cmd , toggle, handler, html){
27776 var event = toggle ? 'toggle' : 'click';
27781 xns: Roo.bootstrap,
27785 enableToggle:toggle !== false,
27787 pressed : toggle ? false : null,
27790 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27791 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27797 // var cb_box = function...
27802 xns: Roo.bootstrap,
27807 xns: Roo.bootstrap,
27811 Roo.each(this.formats, function(f) {
27812 style.menu.items.push({
27814 xns: Roo.bootstrap,
27815 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27820 editorcore.insertTag(this.tagname);
27827 children.push(style);
27829 btn('bold',false,true);
27830 btn('italic',false,true);
27831 btn('align-left', 'justifyleft',true);
27832 btn('align-center', 'justifycenter',true);
27833 btn('align-right' , 'justifyright',true);
27834 btn('link', false, false, function(btn) {
27835 //Roo.log("create link?");
27836 var url = prompt(this.createLinkText, this.defaultLinkValue);
27837 if(url && url != 'http:/'+'/'){
27838 this.editorcore.relayCmd('createlink', url);
27841 btn('list','insertunorderedlist',true);
27842 btn('pencil', false,true, function(btn){
27844 this.toggleSourceEdit(btn.pressed);
27847 if (this.editor.btns.length > 0) {
27848 for (var i = 0; i<this.editor.btns.length; i++) {
27849 children.push(this.editor.btns[i]);
27857 xns: Roo.bootstrap,
27862 xns: Roo.bootstrap,
27867 cog.menu.items.push({
27869 xns: Roo.bootstrap,
27870 html : Clean styles,
27875 editorcore.insertTag(this.tagname);
27884 this.xtype = 'NavSimplebar';
27886 for(var i=0;i< children.length;i++) {
27888 this.buttons.add(this.addxtypeChild(children[i]));
27892 editor.on('editorevent', this.updateToolbar, this);
27894 onBtnClick : function(id)
27896 this.editorcore.relayCmd(id);
27897 this.editorcore.focus();
27901 * Protected method that will not generally be called directly. It triggers
27902 * a toolbar update by reading the markup state of the current selection in the editor.
27904 updateToolbar: function(){
27906 if(!this.editorcore.activated){
27907 this.editor.onFirstFocus(); // is this neeed?
27911 var btns = this.buttons;
27912 var doc = this.editorcore.doc;
27913 btns.get('bold').setActive(doc.queryCommandState('bold'));
27914 btns.get('italic').setActive(doc.queryCommandState('italic'));
27915 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27917 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27918 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27919 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27921 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27922 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27925 var ans = this.editorcore.getAllAncestors();
27926 if (this.formatCombo) {
27929 var store = this.formatCombo.store;
27930 this.formatCombo.setValue("");
27931 for (var i =0; i < ans.length;i++) {
27932 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27934 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27942 // hides menus... - so this cant be on a menu...
27943 Roo.bootstrap.MenuMgr.hideAll();
27945 Roo.bootstrap.MenuMgr.hideAll();
27946 //this.editorsyncValue();
27948 onFirstFocus: function() {
27949 this.buttons.each(function(item){
27953 toggleSourceEdit : function(sourceEditMode){
27956 if(sourceEditMode){
27957 Roo.log("disabling buttons");
27958 this.buttons.each( function(item){
27959 if(item.cmd != 'pencil'){
27965 Roo.log("enabling buttons");
27966 if(this.editorcore.initialized){
27967 this.buttons.each( function(item){
27973 Roo.log("calling toggole on editor");
27974 // tell the editor that it's been pressed..
27975 this.editor.toggleSourceEdit(sourceEditMode);
27989 * @class Roo.bootstrap.Markdown
27990 * @extends Roo.bootstrap.TextArea
27991 * Bootstrap Showdown editable area
27992 * @cfg {string} content
27995 * Create a new Showdown
27998 Roo.bootstrap.Markdown = function(config){
27999 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
28003 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
28007 initEvents : function()
28010 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
28011 this.markdownEl = this.el.createChild({
28012 cls : 'roo-markdown-area'
28014 this.inputEl().addClass('d-none');
28015 if (this.getValue() == '') {
28016 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28019 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28021 this.markdownEl.on('click', this.toggleTextEdit, this);
28022 this.on('blur', this.toggleTextEdit, this);
28023 this.on('specialkey', this.resizeTextArea, this);
28026 toggleTextEdit : function()
28028 var sh = this.markdownEl.getHeight();
28029 this.inputEl().addClass('d-none');
28030 this.markdownEl.addClass('d-none');
28031 if (!this.editing) {
28033 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28034 this.inputEl().removeClass('d-none');
28035 this.inputEl().focus();
28036 this.editing = true;
28039 // show showdown...
28040 this.updateMarkdown();
28041 this.markdownEl.removeClass('d-none');
28042 this.editing = false;
28045 updateMarkdown : function()
28047 if (this.getValue() == '') {
28048 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28052 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28055 resizeTextArea: function () {
28058 Roo.log([sh, this.getValue().split("\n").length * 30]);
28059 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28061 setValue : function(val)
28063 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28064 if (!this.editing) {
28065 this.updateMarkdown();
28071 if (!this.editing) {
28072 this.toggleTextEdit();
28080 * Ext JS Library 1.1.1
28081 * Copyright(c) 2006-2007, Ext JS, LLC.
28083 * Originally Released Under LGPL - original licence link has changed is not relivant.
28086 * <script type="text/javascript">
28090 * @class Roo.bootstrap.PagingToolbar
28091 * @extends Roo.bootstrap.NavSimplebar
28092 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28094 * Create a new PagingToolbar
28095 * @param {Object} config The config object
28096 * @param {Roo.data.Store} store
28098 Roo.bootstrap.PagingToolbar = function(config)
28100 // old args format still supported... - xtype is prefered..
28101 // created from xtype...
28103 this.ds = config.dataSource;
28105 if (config.store && !this.ds) {
28106 this.store= Roo.factory(config.store, Roo.data);
28107 this.ds = this.store;
28108 this.ds.xmodule = this.xmodule || false;
28111 this.toolbarItems = [];
28112 if (config.items) {
28113 this.toolbarItems = config.items;
28116 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28121 this.bind(this.ds);
28124 if (Roo.bootstrap.version == 4) {
28125 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28127 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28132 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28134 * @cfg {Roo.data.Store} dataSource
28135 * The underlying data store providing the paged data
28138 * @cfg {String/HTMLElement/Element} container
28139 * container The id or element that will contain the toolbar
28142 * @cfg {Boolean} displayInfo
28143 * True to display the displayMsg (defaults to false)
28146 * @cfg {Number} pageSize
28147 * The number of records to display per page (defaults to 20)
28151 * @cfg {String} displayMsg
28152 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28154 displayMsg : 'Displaying {0} - {1} of {2}',
28156 * @cfg {String} emptyMsg
28157 * The message to display when no records are found (defaults to "No data to display")
28159 emptyMsg : 'No data to display',
28161 * Customizable piece of the default paging text (defaults to "Page")
28164 beforePageText : "Page",
28166 * Customizable piece of the default paging text (defaults to "of %0")
28169 afterPageText : "of {0}",
28171 * Customizable piece of the default paging text (defaults to "First Page")
28174 firstText : "First Page",
28176 * Customizable piece of the default paging text (defaults to "Previous Page")
28179 prevText : "Previous Page",
28181 * Customizable piece of the default paging text (defaults to "Next Page")
28184 nextText : "Next Page",
28186 * Customizable piece of the default paging text (defaults to "Last Page")
28189 lastText : "Last Page",
28191 * Customizable piece of the default paging text (defaults to "Refresh")
28194 refreshText : "Refresh",
28198 onRender : function(ct, position)
28200 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28201 this.navgroup.parentId = this.id;
28202 this.navgroup.onRender(this.el, null);
28203 // add the buttons to the navgroup
28205 if(this.displayInfo){
28206 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28207 this.displayEl = this.el.select('.x-paging-info', true).first();
28208 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28209 // this.displayEl = navel.el.select('span',true).first();
28215 Roo.each(_this.buttons, function(e){ // this might need to use render????
28216 Roo.factory(e).render(_this.el);
28220 Roo.each(_this.toolbarItems, function(e) {
28221 _this.navgroup.addItem(e);
28225 this.first = this.navgroup.addItem({
28226 tooltip: this.firstText,
28227 cls: "prev btn-outline-secondary",
28228 html : ' <i class="fa fa-step-backward"></i>',
28230 preventDefault: true,
28231 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28234 this.prev = this.navgroup.addItem({
28235 tooltip: this.prevText,
28236 cls: "prev btn-outline-secondary",
28237 html : ' <i class="fa fa-backward"></i>',
28239 preventDefault: true,
28240 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28242 //this.addSeparator();
28245 var field = this.navgroup.addItem( {
28247 cls : 'x-paging-position btn-outline-secondary',
28249 html : this.beforePageText +
28250 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28251 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28254 this.field = field.el.select('input', true).first();
28255 this.field.on("keydown", this.onPagingKeydown, this);
28256 this.field.on("focus", function(){this.dom.select();});
28259 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28260 //this.field.setHeight(18);
28261 //this.addSeparator();
28262 this.next = this.navgroup.addItem({
28263 tooltip: this.nextText,
28264 cls: "next btn-outline-secondary",
28265 html : ' <i class="fa fa-forward"></i>',
28267 preventDefault: true,
28268 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28270 this.last = this.navgroup.addItem({
28271 tooltip: this.lastText,
28272 html : ' <i class="fa fa-step-forward"></i>',
28273 cls: "next btn-outline-secondary",
28275 preventDefault: true,
28276 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28278 //this.addSeparator();
28279 this.loading = this.navgroup.addItem({
28280 tooltip: this.refreshText,
28281 cls: "btn-outline-secondary",
28282 html : ' <i class="fa fa-refresh"></i>',
28283 preventDefault: true,
28284 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28290 updateInfo : function(){
28291 if(this.displayEl){
28292 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28293 var msg = count == 0 ?
28297 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28299 this.displayEl.update(msg);
28304 onLoad : function(ds, r, o)
28306 this.cursor = o.params && o.params.start ? o.params.start : 0;
28308 var d = this.getPageData(),
28313 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28314 this.field.dom.value = ap;
28315 this.first.setDisabled(ap == 1);
28316 this.prev.setDisabled(ap == 1);
28317 this.next.setDisabled(ap == ps);
28318 this.last.setDisabled(ap == ps);
28319 this.loading.enable();
28324 getPageData : function(){
28325 var total = this.ds.getTotalCount();
28328 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28329 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28334 onLoadError : function(){
28335 this.loading.enable();
28339 onPagingKeydown : function(e){
28340 var k = e.getKey();
28341 var d = this.getPageData();
28343 var v = this.field.dom.value, pageNum;
28344 if(!v || isNaN(pageNum = parseInt(v, 10))){
28345 this.field.dom.value = d.activePage;
28348 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28349 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28352 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))
28354 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28355 this.field.dom.value = pageNum;
28356 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28359 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28361 var v = this.field.dom.value, pageNum;
28362 var increment = (e.shiftKey) ? 10 : 1;
28363 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28366 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28367 this.field.dom.value = d.activePage;
28370 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28372 this.field.dom.value = parseInt(v, 10) + increment;
28373 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28374 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28381 beforeLoad : function(){
28383 this.loading.disable();
28388 onClick : function(which){
28397 ds.load({params:{start: 0, limit: this.pageSize}});
28400 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28403 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28406 var total = ds.getTotalCount();
28407 var extra = total % this.pageSize;
28408 var lastStart = extra ? (total - extra) : total-this.pageSize;
28409 ds.load({params:{start: lastStart, limit: this.pageSize}});
28412 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28418 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28419 * @param {Roo.data.Store} store The data store to unbind
28421 unbind : function(ds){
28422 ds.un("beforeload", this.beforeLoad, this);
28423 ds.un("load", this.onLoad, this);
28424 ds.un("loadexception", this.onLoadError, this);
28425 ds.un("remove", this.updateInfo, this);
28426 ds.un("add", this.updateInfo, this);
28427 this.ds = undefined;
28431 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28432 * @param {Roo.data.Store} store The data store to bind
28434 bind : function(ds){
28435 ds.on("beforeload", this.beforeLoad, this);
28436 ds.on("load", this.onLoad, this);
28437 ds.on("loadexception", this.onLoadError, this);
28438 ds.on("remove", this.updateInfo, this);
28439 ds.on("add", this.updateInfo, this);
28450 * @class Roo.bootstrap.MessageBar
28451 * @extends Roo.bootstrap.Component
28452 * Bootstrap MessageBar class
28453 * @cfg {String} html contents of the MessageBar
28454 * @cfg {String} weight (info | success | warning | danger) default info
28455 * @cfg {String} beforeClass insert the bar before the given class
28456 * @cfg {Boolean} closable (true | false) default false
28457 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28460 * Create a new Element
28461 * @param {Object} config The config object
28464 Roo.bootstrap.MessageBar = function(config){
28465 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28468 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28474 beforeClass: 'bootstrap-sticky-wrap',
28476 getAutoCreate : function(){
28480 cls: 'alert alert-dismissable alert-' + this.weight,
28485 html: this.html || ''
28491 cfg.cls += ' alert-messages-fixed';
28505 onRender : function(ct, position)
28507 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28510 var cfg = Roo.apply({}, this.getAutoCreate());
28514 cfg.cls += ' ' + this.cls;
28517 cfg.style = this.style;
28519 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28521 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28524 this.el.select('>button.close').on('click', this.hide, this);
28530 if (!this.rendered) {
28536 this.fireEvent('show', this);
28542 if (!this.rendered) {
28548 this.fireEvent('hide', this);
28551 update : function()
28553 // var e = this.el.dom.firstChild;
28555 // if(this.closable){
28556 // e = e.nextSibling;
28559 // e.data = this.html || '';
28561 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28577 * @class Roo.bootstrap.Graph
28578 * @extends Roo.bootstrap.Component
28579 * Bootstrap Graph class
28583 @cfg {String} graphtype bar | vbar | pie
28584 @cfg {number} g_x coodinator | centre x (pie)
28585 @cfg {number} g_y coodinator | centre y (pie)
28586 @cfg {number} g_r radius (pie)
28587 @cfg {number} g_height height of the chart (respected by all elements in the set)
28588 @cfg {number} g_width width of the chart (respected by all elements in the set)
28589 @cfg {Object} title The title of the chart
28592 -opts (object) options for the chart
28594 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28595 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28597 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.
28598 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28600 o stretch (boolean)
28602 -opts (object) options for the pie
28605 o startAngle (number)
28606 o endAngle (number)
28610 * Create a new Input
28611 * @param {Object} config The config object
28614 Roo.bootstrap.Graph = function(config){
28615 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28621 * The img click event for the img.
28622 * @param {Roo.EventObject} e
28628 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28639 //g_colors: this.colors,
28646 getAutoCreate : function(){
28657 onRender : function(ct,position){
28660 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28662 if (typeof(Raphael) == 'undefined') {
28663 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28667 this.raphael = Raphael(this.el.dom);
28669 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28670 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28671 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28672 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28674 r.text(160, 10, "Single Series Chart").attr(txtattr);
28675 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28676 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28677 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28679 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28680 r.barchart(330, 10, 300, 220, data1);
28681 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28682 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28685 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28686 // r.barchart(30, 30, 560, 250, xdata, {
28687 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28688 // axis : "0 0 1 1",
28689 // axisxlabels : xdata
28690 // //yvalues : cols,
28693 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28695 // this.load(null,xdata,{
28696 // axis : "0 0 1 1",
28697 // axisxlabels : xdata
28702 load : function(graphtype,xdata,opts)
28704 this.raphael.clear();
28706 graphtype = this.graphtype;
28711 var r = this.raphael,
28712 fin = function () {
28713 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28715 fout = function () {
28716 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28718 pfin = function() {
28719 this.sector.stop();
28720 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28723 this.label[0].stop();
28724 this.label[0].attr({ r: 7.5 });
28725 this.label[1].attr({ "font-weight": 800 });
28728 pfout = function() {
28729 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28732 this.label[0].animate({ r: 5 }, 500, "bounce");
28733 this.label[1].attr({ "font-weight": 400 });
28739 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28742 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28745 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28746 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28748 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28755 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28760 setTitle: function(o)
28765 initEvents: function() {
28768 this.el.on('click', this.onClick, this);
28772 onClick : function(e)
28774 Roo.log('img onclick');
28775 this.fireEvent('click', this, e);
28787 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28790 * @class Roo.bootstrap.dash.NumberBox
28791 * @extends Roo.bootstrap.Component
28792 * Bootstrap NumberBox class
28793 * @cfg {String} headline Box headline
28794 * @cfg {String} content Box content
28795 * @cfg {String} icon Box icon
28796 * @cfg {String} footer Footer text
28797 * @cfg {String} fhref Footer href
28800 * Create a new NumberBox
28801 * @param {Object} config The config object
28805 Roo.bootstrap.dash.NumberBox = function(config){
28806 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28810 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28819 getAutoCreate : function(){
28823 cls : 'small-box ',
28831 cls : 'roo-headline',
28832 html : this.headline
28836 cls : 'roo-content',
28837 html : this.content
28851 cls : 'ion ' + this.icon
28860 cls : 'small-box-footer',
28861 href : this.fhref || '#',
28865 cfg.cn.push(footer);
28872 onRender : function(ct,position){
28873 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28880 setHeadline: function (value)
28882 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28885 setFooter: function (value, href)
28887 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28890 this.el.select('a.small-box-footer',true).first().attr('href', href);
28895 setContent: function (value)
28897 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28900 initEvents: function()
28914 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28917 * @class Roo.bootstrap.dash.TabBox
28918 * @extends Roo.bootstrap.Component
28919 * Bootstrap TabBox class
28920 * @cfg {String} title Title of the TabBox
28921 * @cfg {String} icon Icon of the TabBox
28922 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28923 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28926 * Create a new TabBox
28927 * @param {Object} config The config object
28931 Roo.bootstrap.dash.TabBox = function(config){
28932 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28937 * When a pane is added
28938 * @param {Roo.bootstrap.dash.TabPane} pane
28942 * @event activatepane
28943 * When a pane is activated
28944 * @param {Roo.bootstrap.dash.TabPane} pane
28946 "activatepane" : true
28954 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28959 tabScrollable : false,
28961 getChildContainer : function()
28963 return this.el.select('.tab-content', true).first();
28966 getAutoCreate : function(){
28970 cls: 'pull-left header',
28978 cls: 'fa ' + this.icon
28984 cls: 'nav nav-tabs pull-right',
28990 if(this.tabScrollable){
28997 cls: 'nav nav-tabs pull-right',
29008 cls: 'nav-tabs-custom',
29013 cls: 'tab-content no-padding',
29021 initEvents : function()
29023 //Roo.log('add add pane handler');
29024 this.on('addpane', this.onAddPane, this);
29027 * Updates the box title
29028 * @param {String} html to set the title to.
29030 setTitle : function(value)
29032 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29034 onAddPane : function(pane)
29036 this.panes.push(pane);
29037 //Roo.log('addpane');
29039 // tabs are rendere left to right..
29040 if(!this.showtabs){
29044 var ctr = this.el.select('.nav-tabs', true).first();
29047 var existing = ctr.select('.nav-tab',true);
29048 var qty = existing.getCount();;
29051 var tab = ctr.createChild({
29053 cls : 'nav-tab' + (qty ? '' : ' active'),
29061 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29064 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29066 pane.el.addClass('active');
29071 onTabClick : function(ev,un,ob,pane)
29073 //Roo.log('tab - prev default');
29074 ev.preventDefault();
29077 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29078 pane.tab.addClass('active');
29079 //Roo.log(pane.title);
29080 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29081 // technically we should have a deactivate event.. but maybe add later.
29082 // and it should not de-activate the selected tab...
29083 this.fireEvent('activatepane', pane);
29084 pane.el.addClass('active');
29085 pane.fireEvent('activate');
29090 getActivePane : function()
29093 Roo.each(this.panes, function(p) {
29094 if(p.el.hasClass('active')){
29115 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29117 * @class Roo.bootstrap.TabPane
29118 * @extends Roo.bootstrap.Component
29119 * Bootstrap TabPane class
29120 * @cfg {Boolean} active (false | true) Default false
29121 * @cfg {String} title title of panel
29125 * Create a new TabPane
29126 * @param {Object} config The config object
29129 Roo.bootstrap.dash.TabPane = function(config){
29130 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29136 * When a pane is activated
29137 * @param {Roo.bootstrap.dash.TabPane} pane
29144 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29149 // the tabBox that this is attached to.
29152 getAutoCreate : function()
29160 cfg.cls += ' active';
29165 initEvents : function()
29167 //Roo.log('trigger add pane handler');
29168 this.parent().fireEvent('addpane', this)
29172 * Updates the tab title
29173 * @param {String} html to set the title to.
29175 setTitle: function(str)
29181 this.tab.select('a', true).first().dom.innerHTML = str;
29198 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29201 * @class Roo.bootstrap.menu.Menu
29202 * @extends Roo.bootstrap.Component
29203 * Bootstrap Menu class - container for Menu
29204 * @cfg {String} html Text of the menu
29205 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29206 * @cfg {String} icon Font awesome icon
29207 * @cfg {String} pos Menu align to (top | bottom) default bottom
29211 * Create a new Menu
29212 * @param {Object} config The config object
29216 Roo.bootstrap.menu.Menu = function(config){
29217 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29221 * @event beforeshow
29222 * Fires before this menu is displayed
29223 * @param {Roo.bootstrap.menu.Menu} this
29227 * @event beforehide
29228 * Fires before this menu is hidden
29229 * @param {Roo.bootstrap.menu.Menu} this
29234 * Fires after this menu is displayed
29235 * @param {Roo.bootstrap.menu.Menu} this
29240 * Fires after this menu is hidden
29241 * @param {Roo.bootstrap.menu.Menu} this
29246 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29247 * @param {Roo.bootstrap.menu.Menu} this
29248 * @param {Roo.EventObject} e
29255 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
29259 weight : 'default',
29264 getChildContainer : function() {
29265 if(this.isSubMenu){
29269 return this.el.select('ul.dropdown-menu', true).first();
29272 getAutoCreate : function()
29277 cls : 'roo-menu-text',
29285 cls : 'fa ' + this.icon
29296 cls : 'dropdown-button btn btn-' + this.weight,
29301 cls : 'dropdown-toggle btn btn-' + this.weight,
29311 cls : 'dropdown-menu'
29317 if(this.pos == 'top'){
29318 cfg.cls += ' dropup';
29321 if(this.isSubMenu){
29324 cls : 'dropdown-menu'
29331 onRender : function(ct, position)
29333 this.isSubMenu = ct.hasClass('dropdown-submenu');
29335 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29338 initEvents : function()
29340 if(this.isSubMenu){
29344 this.hidden = true;
29346 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29347 this.triggerEl.on('click', this.onTriggerPress, this);
29349 this.buttonEl = this.el.select('button.dropdown-button', true).first();
29350 this.buttonEl.on('click', this.onClick, this);
29356 if(this.isSubMenu){
29360 return this.el.select('ul.dropdown-menu', true).first();
29363 onClick : function(e)
29365 this.fireEvent("click", this, e);
29368 onTriggerPress : function(e)
29370 if (this.isVisible()) {
29377 isVisible : function(){
29378 return !this.hidden;
29383 this.fireEvent("beforeshow", this);
29385 this.hidden = false;
29386 this.el.addClass('open');
29388 Roo.get(document).on("mouseup", this.onMouseUp, this);
29390 this.fireEvent("show", this);
29397 this.fireEvent("beforehide", this);
29399 this.hidden = true;
29400 this.el.removeClass('open');
29402 Roo.get(document).un("mouseup", this.onMouseUp);
29404 this.fireEvent("hide", this);
29407 onMouseUp : function()
29421 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29424 * @class Roo.bootstrap.menu.Item
29425 * @extends Roo.bootstrap.Component
29426 * Bootstrap MenuItem class
29427 * @cfg {Boolean} submenu (true | false) default false
29428 * @cfg {String} html text of the item
29429 * @cfg {String} href the link
29430 * @cfg {Boolean} disable (true | false) default false
29431 * @cfg {Boolean} preventDefault (true | false) default true
29432 * @cfg {String} icon Font awesome icon
29433 * @cfg {String} pos Submenu align to (left | right) default right
29437 * Create a new Item
29438 * @param {Object} config The config object
29442 Roo.bootstrap.menu.Item = function(config){
29443 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29447 * Fires when the mouse is hovering over this menu
29448 * @param {Roo.bootstrap.menu.Item} this
29449 * @param {Roo.EventObject} e
29454 * Fires when the mouse exits this menu
29455 * @param {Roo.bootstrap.menu.Item} this
29456 * @param {Roo.EventObject} e
29462 * The raw click event for the entire grid.
29463 * @param {Roo.EventObject} e
29469 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
29474 preventDefault: true,
29479 getAutoCreate : function()
29484 cls : 'roo-menu-item-text',
29492 cls : 'fa ' + this.icon
29501 href : this.href || '#',
29508 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29512 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29514 if(this.pos == 'left'){
29515 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29522 initEvents : function()
29524 this.el.on('mouseover', this.onMouseOver, this);
29525 this.el.on('mouseout', this.onMouseOut, this);
29527 this.el.select('a', true).first().on('click', this.onClick, this);
29531 onClick : function(e)
29533 if(this.preventDefault){
29534 e.preventDefault();
29537 this.fireEvent("click", this, e);
29540 onMouseOver : function(e)
29542 if(this.submenu && this.pos == 'left'){
29543 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29546 this.fireEvent("mouseover", this, e);
29549 onMouseOut : function(e)
29551 this.fireEvent("mouseout", this, e);
29563 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29566 * @class Roo.bootstrap.menu.Separator
29567 * @extends Roo.bootstrap.Component
29568 * Bootstrap Separator class
29571 * Create a new Separator
29572 * @param {Object} config The config object
29576 Roo.bootstrap.menu.Separator = function(config){
29577 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29580 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29582 getAutoCreate : function(){
29585 cls: 'dropdown-divider divider'
29603 * @class Roo.bootstrap.Tooltip
29604 * Bootstrap Tooltip class
29605 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29606 * to determine which dom element triggers the tooltip.
29608 * It needs to add support for additional attributes like tooltip-position
29611 * Create a new Toolti
29612 * @param {Object} config The config object
29615 Roo.bootstrap.Tooltip = function(config){
29616 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29618 this.alignment = Roo.bootstrap.Tooltip.alignment;
29620 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29621 this.alignment = config.alignment;
29626 Roo.apply(Roo.bootstrap.Tooltip, {
29628 * @function init initialize tooltip monitoring.
29632 currentTip : false,
29633 currentRegion : false,
29639 Roo.get(document).on('mouseover', this.enter ,this);
29640 Roo.get(document).on('mouseout', this.leave, this);
29643 this.currentTip = new Roo.bootstrap.Tooltip();
29646 enter : function(ev)
29648 var dom = ev.getTarget();
29650 //Roo.log(['enter',dom]);
29651 var el = Roo.fly(dom);
29652 if (this.currentEl) {
29654 //Roo.log(this.currentEl);
29655 //Roo.log(this.currentEl.contains(dom));
29656 if (this.currentEl == el) {
29659 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29665 if (this.currentTip.el) {
29666 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29670 if(!el || el.dom == document){
29676 if (!el.attr('tooltip')) {
29677 pel = el.findParent("[tooltip]");
29679 bindEl = Roo.get(pel);
29685 // you can not look for children, as if el is the body.. then everythign is the child..
29686 if (!pel && !el.attr('tooltip')) { //
29687 if (!el.select("[tooltip]").elements.length) {
29690 // is the mouse over this child...?
29691 bindEl = el.select("[tooltip]").first();
29692 var xy = ev.getXY();
29693 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29694 //Roo.log("not in region.");
29697 //Roo.log("child element over..");
29700 this.currentEl = el;
29701 this.currentTip.bind(bindEl);
29702 this.currentRegion = Roo.lib.Region.getRegion(dom);
29703 this.currentTip.enter();
29706 leave : function(ev)
29708 var dom = ev.getTarget();
29709 //Roo.log(['leave',dom]);
29710 if (!this.currentEl) {
29715 if (dom != this.currentEl.dom) {
29718 var xy = ev.getXY();
29719 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29722 // only activate leave if mouse cursor is outside... bounding box..
29727 if (this.currentTip) {
29728 this.currentTip.leave();
29730 //Roo.log('clear currentEl');
29731 this.currentEl = false;
29736 'left' : ['r-l', [-2,0], 'right'],
29737 'right' : ['l-r', [2,0], 'left'],
29738 'bottom' : ['t-b', [0,2], 'top'],
29739 'top' : [ 'b-t', [0,-2], 'bottom']
29745 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29750 delay : null, // can be { show : 300 , hide: 500}
29754 hoverState : null, //???
29756 placement : 'bottom',
29760 getAutoCreate : function(){
29767 cls : 'tooltip-arrow arrow'
29770 cls : 'tooltip-inner'
29777 bind : function(el)
29782 initEvents : function()
29784 this.arrowEl = this.el.select('.arrow', true).first();
29785 this.innerEl = this.el.select('.tooltip-inner', true).first();
29788 enter : function () {
29790 if (this.timeout != null) {
29791 clearTimeout(this.timeout);
29794 this.hoverState = 'in';
29795 //Roo.log("enter - show");
29796 if (!this.delay || !this.delay.show) {
29801 this.timeout = setTimeout(function () {
29802 if (_t.hoverState == 'in') {
29805 }, this.delay.show);
29809 clearTimeout(this.timeout);
29811 this.hoverState = 'out';
29812 if (!this.delay || !this.delay.hide) {
29818 this.timeout = setTimeout(function () {
29819 //Roo.log("leave - timeout");
29821 if (_t.hoverState == 'out') {
29823 Roo.bootstrap.Tooltip.currentEl = false;
29828 show : function (msg)
29831 this.render(document.body);
29834 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29836 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29838 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29840 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29841 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29843 var placement = typeof this.placement == 'function' ?
29844 this.placement.call(this, this.el, on_el) :
29847 var autoToken = /\s?auto?\s?/i;
29848 var autoPlace = autoToken.test(placement);
29850 placement = placement.replace(autoToken, '') || 'top';
29854 //this.el.setXY([0,0]);
29856 //this.el.dom.style.display='block';
29858 //this.el.appendTo(on_el);
29860 var p = this.getPosition();
29861 var box = this.el.getBox();
29867 var align = this.alignment[placement];
29869 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29871 if(placement == 'top' || placement == 'bottom'){
29873 placement = 'right';
29876 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29877 placement = 'left';
29880 var scroll = Roo.select('body', true).first().getScroll();
29882 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29886 align = this.alignment[placement];
29888 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29892 var elems = document.getElementsByTagName('div');
29893 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29894 for (var i = 0; i < elems.length; i++) {
29895 var zindex = Number.parseInt(
29896 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29899 if (zindex > highest) {
29906 this.el.dom.style.zIndex = highest;
29908 this.el.alignTo(this.bindEl, align[0],align[1]);
29909 //var arrow = this.el.select('.arrow',true).first();
29910 //arrow.set(align[2],
29912 this.el.addClass(placement);
29913 this.el.addClass("bs-tooltip-"+ placement);
29915 this.el.addClass('in fade show');
29917 this.hoverState = null;
29919 if (this.el.hasClass('fade')) {
29934 //this.el.setXY([0,0]);
29935 this.el.removeClass(['show', 'in']);
29951 * @class Roo.bootstrap.LocationPicker
29952 * @extends Roo.bootstrap.Component
29953 * Bootstrap LocationPicker class
29954 * @cfg {Number} latitude Position when init default 0
29955 * @cfg {Number} longitude Position when init default 0
29956 * @cfg {Number} zoom default 15
29957 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29958 * @cfg {Boolean} mapTypeControl default false
29959 * @cfg {Boolean} disableDoubleClickZoom default false
29960 * @cfg {Boolean} scrollwheel default true
29961 * @cfg {Boolean} streetViewControl default false
29962 * @cfg {Number} radius default 0
29963 * @cfg {String} locationName
29964 * @cfg {Boolean} draggable default true
29965 * @cfg {Boolean} enableAutocomplete default false
29966 * @cfg {Boolean} enableReverseGeocode default true
29967 * @cfg {String} markerTitle
29970 * Create a new LocationPicker
29971 * @param {Object} config The config object
29975 Roo.bootstrap.LocationPicker = function(config){
29977 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29982 * Fires when the picker initialized.
29983 * @param {Roo.bootstrap.LocationPicker} this
29984 * @param {Google Location} location
29988 * @event positionchanged
29989 * Fires when the picker position changed.
29990 * @param {Roo.bootstrap.LocationPicker} this
29991 * @param {Google Location} location
29993 positionchanged : true,
29996 * Fires when the map resize.
29997 * @param {Roo.bootstrap.LocationPicker} this
30002 * Fires when the map show.
30003 * @param {Roo.bootstrap.LocationPicker} this
30008 * Fires when the map hide.
30009 * @param {Roo.bootstrap.LocationPicker} this
30014 * Fires when click the map.
30015 * @param {Roo.bootstrap.LocationPicker} this
30016 * @param {Map event} e
30020 * @event mapRightClick
30021 * Fires when right click the map.
30022 * @param {Roo.bootstrap.LocationPicker} this
30023 * @param {Map event} e
30025 mapRightClick : true,
30027 * @event markerClick
30028 * Fires when click the marker.
30029 * @param {Roo.bootstrap.LocationPicker} this
30030 * @param {Map event} e
30032 markerClick : true,
30034 * @event markerRightClick
30035 * Fires when right click the marker.
30036 * @param {Roo.bootstrap.LocationPicker} this
30037 * @param {Map event} e
30039 markerRightClick : true,
30041 * @event OverlayViewDraw
30042 * Fires when OverlayView Draw
30043 * @param {Roo.bootstrap.LocationPicker} this
30045 OverlayViewDraw : true,
30047 * @event OverlayViewOnAdd
30048 * Fires when OverlayView Draw
30049 * @param {Roo.bootstrap.LocationPicker} this
30051 OverlayViewOnAdd : true,
30053 * @event OverlayViewOnRemove
30054 * Fires when OverlayView Draw
30055 * @param {Roo.bootstrap.LocationPicker} this
30057 OverlayViewOnRemove : true,
30059 * @event OverlayViewShow
30060 * Fires when OverlayView Draw
30061 * @param {Roo.bootstrap.LocationPicker} this
30062 * @param {Pixel} cpx
30064 OverlayViewShow : true,
30066 * @event OverlayViewHide
30067 * Fires when OverlayView Draw
30068 * @param {Roo.bootstrap.LocationPicker} this
30070 OverlayViewHide : true,
30072 * @event loadexception
30073 * Fires when load google lib failed.
30074 * @param {Roo.bootstrap.LocationPicker} this
30076 loadexception : true
30081 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30083 gMapContext: false,
30089 mapTypeControl: false,
30090 disableDoubleClickZoom: false,
30092 streetViewControl: false,
30096 enableAutocomplete: false,
30097 enableReverseGeocode: true,
30100 getAutoCreate: function()
30105 cls: 'roo-location-picker'
30111 initEvents: function(ct, position)
30113 if(!this.el.getWidth() || this.isApplied()){
30117 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30122 initial: function()
30124 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30125 this.fireEvent('loadexception', this);
30129 if(!this.mapTypeId){
30130 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30133 this.gMapContext = this.GMapContext();
30135 this.initOverlayView();
30137 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30141 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30142 _this.setPosition(_this.gMapContext.marker.position);
30145 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30146 _this.fireEvent('mapClick', this, event);
30150 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30151 _this.fireEvent('mapRightClick', this, event);
30155 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30156 _this.fireEvent('markerClick', this, event);
30160 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30161 _this.fireEvent('markerRightClick', this, event);
30165 this.setPosition(this.gMapContext.location);
30167 this.fireEvent('initial', this, this.gMapContext.location);
30170 initOverlayView: function()
30174 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30178 _this.fireEvent('OverlayViewDraw', _this);
30183 _this.fireEvent('OverlayViewOnAdd', _this);
30186 onRemove: function()
30188 _this.fireEvent('OverlayViewOnRemove', _this);
30191 show: function(cpx)
30193 _this.fireEvent('OverlayViewShow', _this, cpx);
30198 _this.fireEvent('OverlayViewHide', _this);
30204 fromLatLngToContainerPixel: function(event)
30206 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30209 isApplied: function()
30211 return this.getGmapContext() == false ? false : true;
30214 getGmapContext: function()
30216 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30219 GMapContext: function()
30221 var position = new google.maps.LatLng(this.latitude, this.longitude);
30223 var _map = new google.maps.Map(this.el.dom, {
30226 mapTypeId: this.mapTypeId,
30227 mapTypeControl: this.mapTypeControl,
30228 disableDoubleClickZoom: this.disableDoubleClickZoom,
30229 scrollwheel: this.scrollwheel,
30230 streetViewControl: this.streetViewControl,
30231 locationName: this.locationName,
30232 draggable: this.draggable,
30233 enableAutocomplete: this.enableAutocomplete,
30234 enableReverseGeocode: this.enableReverseGeocode
30237 var _marker = new google.maps.Marker({
30238 position: position,
30240 title: this.markerTitle,
30241 draggable: this.draggable
30248 location: position,
30249 radius: this.radius,
30250 locationName: this.locationName,
30251 addressComponents: {
30252 formatted_address: null,
30253 addressLine1: null,
30254 addressLine2: null,
30256 streetNumber: null,
30260 stateOrProvince: null
30263 domContainer: this.el.dom,
30264 geodecoder: new google.maps.Geocoder()
30268 drawCircle: function(center, radius, options)
30270 if (this.gMapContext.circle != null) {
30271 this.gMapContext.circle.setMap(null);
30275 options = Roo.apply({}, options, {
30276 strokeColor: "#0000FF",
30277 strokeOpacity: .35,
30279 fillColor: "#0000FF",
30283 options.map = this.gMapContext.map;
30284 options.radius = radius;
30285 options.center = center;
30286 this.gMapContext.circle = new google.maps.Circle(options);
30287 return this.gMapContext.circle;
30293 setPosition: function(location)
30295 this.gMapContext.location = location;
30296 this.gMapContext.marker.setPosition(location);
30297 this.gMapContext.map.panTo(location);
30298 this.drawCircle(location, this.gMapContext.radius, {});
30302 if (this.gMapContext.settings.enableReverseGeocode) {
30303 this.gMapContext.geodecoder.geocode({
30304 latLng: this.gMapContext.location
30305 }, function(results, status) {
30307 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30308 _this.gMapContext.locationName = results[0].formatted_address;
30309 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30311 _this.fireEvent('positionchanged', this, location);
30318 this.fireEvent('positionchanged', this, location);
30323 google.maps.event.trigger(this.gMapContext.map, "resize");
30325 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30327 this.fireEvent('resize', this);
30330 setPositionByLatLng: function(latitude, longitude)
30332 this.setPosition(new google.maps.LatLng(latitude, longitude));
30335 getCurrentPosition: function()
30338 latitude: this.gMapContext.location.lat(),
30339 longitude: this.gMapContext.location.lng()
30343 getAddressName: function()
30345 return this.gMapContext.locationName;
30348 getAddressComponents: function()
30350 return this.gMapContext.addressComponents;
30353 address_component_from_google_geocode: function(address_components)
30357 for (var i = 0; i < address_components.length; i++) {
30358 var component = address_components[i];
30359 if (component.types.indexOf("postal_code") >= 0) {
30360 result.postalCode = component.short_name;
30361 } else if (component.types.indexOf("street_number") >= 0) {
30362 result.streetNumber = component.short_name;
30363 } else if (component.types.indexOf("route") >= 0) {
30364 result.streetName = component.short_name;
30365 } else if (component.types.indexOf("neighborhood") >= 0) {
30366 result.city = component.short_name;
30367 } else if (component.types.indexOf("locality") >= 0) {
30368 result.city = component.short_name;
30369 } else if (component.types.indexOf("sublocality") >= 0) {
30370 result.district = component.short_name;
30371 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30372 result.stateOrProvince = component.short_name;
30373 } else if (component.types.indexOf("country") >= 0) {
30374 result.country = component.short_name;
30378 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30379 result.addressLine2 = "";
30383 setZoomLevel: function(zoom)
30385 this.gMapContext.map.setZoom(zoom);
30398 this.fireEvent('show', this);
30409 this.fireEvent('hide', this);
30414 Roo.apply(Roo.bootstrap.LocationPicker, {
30416 OverlayView : function(map, options)
30418 options = options || {};
30425 * @class Roo.bootstrap.Alert
30426 * @extends Roo.bootstrap.Component
30427 * Bootstrap Alert class - shows an alert area box
30429 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30430 Enter a valid email address
30433 * @cfg {String} title The title of alert
30434 * @cfg {String} html The content of alert
30435 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30436 * @cfg {String} fa font-awesomeicon
30437 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30438 * @cfg {Boolean} close true to show a x closer
30442 * Create a new alert
30443 * @param {Object} config The config object
30447 Roo.bootstrap.Alert = function(config){
30448 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30452 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30458 faicon: false, // BC
30462 getAutoCreate : function()
30474 style : this.close ? '' : 'display:none'
30478 cls : 'roo-alert-icon'
30483 cls : 'roo-alert-title',
30488 cls : 'roo-alert-text',
30495 cfg.cn[0].cls += ' fa ' + this.faicon;
30498 cfg.cn[0].cls += ' fa ' + this.fa;
30502 cfg.cls += ' alert-' + this.weight;
30508 initEvents: function()
30510 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30511 this.titleEl = this.el.select('.roo-alert-title',true).first();
30512 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30513 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30514 if (this.seconds > 0) {
30515 this.hide.defer(this.seconds, this);
30519 * Set the Title Message HTML
30520 * @param {String} html
30522 setTitle : function(str)
30524 this.titleEl.dom.innerHTML = str;
30528 * Set the Body Message HTML
30529 * @param {String} html
30531 setHtml : function(str)
30533 this.htmlEl.dom.innerHTML = str;
30536 * Set the Weight of the alert
30537 * @param {String} (success|info|warning|danger) weight
30540 setWeight : function(weight)
30543 this.el.removeClass('alert-' + this.weight);
30546 this.weight = weight;
30548 this.el.addClass('alert-' + this.weight);
30551 * Set the Icon of the alert
30552 * @param {String} see fontawsome names (name without the 'fa-' bit)
30554 setIcon : function(icon)
30557 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30560 this.faicon = icon;
30562 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30587 * @class Roo.bootstrap.UploadCropbox
30588 * @extends Roo.bootstrap.Component
30589 * Bootstrap UploadCropbox class
30590 * @cfg {String} emptyText show when image has been loaded
30591 * @cfg {String} rotateNotify show when image too small to rotate
30592 * @cfg {Number} errorTimeout default 3000
30593 * @cfg {Number} minWidth default 300
30594 * @cfg {Number} minHeight default 300
30595 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30596 * @cfg {Boolean} isDocument (true|false) default false
30597 * @cfg {String} url action url
30598 * @cfg {String} paramName default 'imageUpload'
30599 * @cfg {String} method default POST
30600 * @cfg {Boolean} loadMask (true|false) default true
30601 * @cfg {Boolean} loadingText default 'Loading...'
30604 * Create a new UploadCropbox
30605 * @param {Object} config The config object
30608 Roo.bootstrap.UploadCropbox = function(config){
30609 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30613 * @event beforeselectfile
30614 * Fire before select file
30615 * @param {Roo.bootstrap.UploadCropbox} this
30617 "beforeselectfile" : true,
30620 * Fire after initEvent
30621 * @param {Roo.bootstrap.UploadCropbox} this
30626 * Fire after initEvent
30627 * @param {Roo.bootstrap.UploadCropbox} this
30628 * @param {String} data
30633 * Fire when preparing the file data
30634 * @param {Roo.bootstrap.UploadCropbox} this
30635 * @param {Object} file
30640 * Fire when get exception
30641 * @param {Roo.bootstrap.UploadCropbox} this
30642 * @param {XMLHttpRequest} xhr
30644 "exception" : true,
30646 * @event beforeloadcanvas
30647 * Fire before load the canvas
30648 * @param {Roo.bootstrap.UploadCropbox} this
30649 * @param {String} src
30651 "beforeloadcanvas" : true,
30654 * Fire when trash image
30655 * @param {Roo.bootstrap.UploadCropbox} this
30660 * Fire when download the image
30661 * @param {Roo.bootstrap.UploadCropbox} this
30665 * @event footerbuttonclick
30666 * Fire when footerbuttonclick
30667 * @param {Roo.bootstrap.UploadCropbox} this
30668 * @param {String} type
30670 "footerbuttonclick" : true,
30674 * @param {Roo.bootstrap.UploadCropbox} this
30679 * Fire when rotate the image
30680 * @param {Roo.bootstrap.UploadCropbox} this
30681 * @param {String} pos
30686 * Fire when inspect the file
30687 * @param {Roo.bootstrap.UploadCropbox} this
30688 * @param {Object} file
30693 * Fire when xhr upload the file
30694 * @param {Roo.bootstrap.UploadCropbox} this
30695 * @param {Object} data
30700 * Fire when arrange the file data
30701 * @param {Roo.bootstrap.UploadCropbox} this
30702 * @param {Object} formData
30707 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30710 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30712 emptyText : 'Click to upload image',
30713 rotateNotify : 'Image is too small to rotate',
30714 errorTimeout : 3000,
30728 cropType : 'image/jpeg',
30730 canvasLoaded : false,
30731 isDocument : false,
30733 paramName : 'imageUpload',
30735 loadingText : 'Loading...',
30738 getAutoCreate : function()
30742 cls : 'roo-upload-cropbox',
30746 cls : 'roo-upload-cropbox-selector',
30751 cls : 'roo-upload-cropbox-body',
30752 style : 'cursor:pointer',
30756 cls : 'roo-upload-cropbox-preview'
30760 cls : 'roo-upload-cropbox-thumb'
30764 cls : 'roo-upload-cropbox-empty-notify',
30765 html : this.emptyText
30769 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30770 html : this.rotateNotify
30776 cls : 'roo-upload-cropbox-footer',
30779 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30789 onRender : function(ct, position)
30791 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30793 if (this.buttons.length) {
30795 Roo.each(this.buttons, function(bb) {
30797 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30799 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30805 this.maskEl = this.el;
30809 initEvents : function()
30811 this.urlAPI = (window.createObjectURL && window) ||
30812 (window.URL && URL.revokeObjectURL && URL) ||
30813 (window.webkitURL && webkitURL);
30815 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30816 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30818 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30819 this.selectorEl.hide();
30821 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30822 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30824 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30825 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30826 this.thumbEl.hide();
30828 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30829 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30831 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30832 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30833 this.errorEl.hide();
30835 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30836 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30837 this.footerEl.hide();
30839 this.setThumbBoxSize();
30845 this.fireEvent('initial', this);
30852 window.addEventListener("resize", function() { _this.resize(); } );
30854 this.bodyEl.on('click', this.beforeSelectFile, this);
30857 this.bodyEl.on('touchstart', this.onTouchStart, this);
30858 this.bodyEl.on('touchmove', this.onTouchMove, this);
30859 this.bodyEl.on('touchend', this.onTouchEnd, this);
30863 this.bodyEl.on('mousedown', this.onMouseDown, this);
30864 this.bodyEl.on('mousemove', this.onMouseMove, this);
30865 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30866 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30867 Roo.get(document).on('mouseup', this.onMouseUp, this);
30870 this.selectorEl.on('change', this.onFileSelected, this);
30876 this.baseScale = 1;
30878 this.baseRotate = 1;
30879 this.dragable = false;
30880 this.pinching = false;
30883 this.cropData = false;
30884 this.notifyEl.dom.innerHTML = this.emptyText;
30886 this.selectorEl.dom.value = '';
30890 resize : function()
30892 if(this.fireEvent('resize', this) != false){
30893 this.setThumbBoxPosition();
30894 this.setCanvasPosition();
30898 onFooterButtonClick : function(e, el, o, type)
30901 case 'rotate-left' :
30902 this.onRotateLeft(e);
30904 case 'rotate-right' :
30905 this.onRotateRight(e);
30908 this.beforeSelectFile(e);
30923 this.fireEvent('footerbuttonclick', this, type);
30926 beforeSelectFile : function(e)
30928 e.preventDefault();
30930 if(this.fireEvent('beforeselectfile', this) != false){
30931 this.selectorEl.dom.click();
30935 onFileSelected : function(e)
30937 e.preventDefault();
30939 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30943 var file = this.selectorEl.dom.files[0];
30945 if(this.fireEvent('inspect', this, file) != false){
30946 this.prepare(file);
30951 trash : function(e)
30953 this.fireEvent('trash', this);
30956 download : function(e)
30958 this.fireEvent('download', this);
30961 loadCanvas : function(src)
30963 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30967 this.imageEl = document.createElement('img');
30971 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30973 this.imageEl.src = src;
30977 onLoadCanvas : function()
30979 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30980 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30982 this.bodyEl.un('click', this.beforeSelectFile, this);
30984 this.notifyEl.hide();
30985 this.thumbEl.show();
30986 this.footerEl.show();
30988 this.baseRotateLevel();
30990 if(this.isDocument){
30991 this.setThumbBoxSize();
30994 this.setThumbBoxPosition();
30996 this.baseScaleLevel();
31002 this.canvasLoaded = true;
31005 this.maskEl.unmask();
31010 setCanvasPosition : function()
31012 if(!this.canvasEl){
31016 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31017 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31019 this.previewEl.setLeft(pw);
31020 this.previewEl.setTop(ph);
31024 onMouseDown : function(e)
31028 this.dragable = true;
31029 this.pinching = false;
31031 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31032 this.dragable = false;
31036 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31037 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31041 onMouseMove : function(e)
31045 if(!this.canvasLoaded){
31049 if (!this.dragable){
31053 var minX = Math.ceil(this.thumbEl.getLeft(true));
31054 var minY = Math.ceil(this.thumbEl.getTop(true));
31056 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31057 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31059 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31060 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31062 x = x - this.mouseX;
31063 y = y - this.mouseY;
31065 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31066 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31068 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31069 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31071 this.previewEl.setLeft(bgX);
31072 this.previewEl.setTop(bgY);
31074 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31075 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31078 onMouseUp : function(e)
31082 this.dragable = false;
31085 onMouseWheel : function(e)
31089 this.startScale = this.scale;
31091 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31093 if(!this.zoomable()){
31094 this.scale = this.startScale;
31103 zoomable : function()
31105 var minScale = this.thumbEl.getWidth() / this.minWidth;
31107 if(this.minWidth < this.minHeight){
31108 minScale = this.thumbEl.getHeight() / this.minHeight;
31111 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31112 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31116 (this.rotate == 0 || this.rotate == 180) &&
31118 width > this.imageEl.OriginWidth ||
31119 height > this.imageEl.OriginHeight ||
31120 (width < this.minWidth && height < this.minHeight)
31128 (this.rotate == 90 || this.rotate == 270) &&
31130 width > this.imageEl.OriginWidth ||
31131 height > this.imageEl.OriginHeight ||
31132 (width < this.minHeight && height < this.minWidth)
31139 !this.isDocument &&
31140 (this.rotate == 0 || this.rotate == 180) &&
31142 width < this.minWidth ||
31143 width > this.imageEl.OriginWidth ||
31144 height < this.minHeight ||
31145 height > this.imageEl.OriginHeight
31152 !this.isDocument &&
31153 (this.rotate == 90 || this.rotate == 270) &&
31155 width < this.minHeight ||
31156 width > this.imageEl.OriginWidth ||
31157 height < this.minWidth ||
31158 height > this.imageEl.OriginHeight
31168 onRotateLeft : function(e)
31170 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31172 var minScale = this.thumbEl.getWidth() / this.minWidth;
31174 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31175 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31177 this.startScale = this.scale;
31179 while (this.getScaleLevel() < minScale){
31181 this.scale = this.scale + 1;
31183 if(!this.zoomable()){
31188 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31189 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31194 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31201 this.scale = this.startScale;
31203 this.onRotateFail();
31208 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31210 if(this.isDocument){
31211 this.setThumbBoxSize();
31212 this.setThumbBoxPosition();
31213 this.setCanvasPosition();
31218 this.fireEvent('rotate', this, 'left');
31222 onRotateRight : function(e)
31224 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31226 var minScale = this.thumbEl.getWidth() / this.minWidth;
31228 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31229 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31231 this.startScale = this.scale;
31233 while (this.getScaleLevel() < minScale){
31235 this.scale = this.scale + 1;
31237 if(!this.zoomable()){
31242 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31243 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31248 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31255 this.scale = this.startScale;
31257 this.onRotateFail();
31262 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31264 if(this.isDocument){
31265 this.setThumbBoxSize();
31266 this.setThumbBoxPosition();
31267 this.setCanvasPosition();
31272 this.fireEvent('rotate', this, 'right');
31275 onRotateFail : function()
31277 this.errorEl.show(true);
31281 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31286 this.previewEl.dom.innerHTML = '';
31288 var canvasEl = document.createElement("canvas");
31290 var contextEl = canvasEl.getContext("2d");
31292 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31293 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31294 var center = this.imageEl.OriginWidth / 2;
31296 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31297 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31298 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31299 center = this.imageEl.OriginHeight / 2;
31302 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31304 contextEl.translate(center, center);
31305 contextEl.rotate(this.rotate * Math.PI / 180);
31307 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31309 this.canvasEl = document.createElement("canvas");
31311 this.contextEl = this.canvasEl.getContext("2d");
31313 switch (this.rotate) {
31316 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31317 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31319 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31324 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31325 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31327 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31328 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);
31332 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31337 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31338 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31340 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31341 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);
31345 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);
31350 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31351 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31353 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31354 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31358 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);
31365 this.previewEl.appendChild(this.canvasEl);
31367 this.setCanvasPosition();
31372 if(!this.canvasLoaded){
31376 var imageCanvas = document.createElement("canvas");
31378 var imageContext = imageCanvas.getContext("2d");
31380 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31381 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31383 var center = imageCanvas.width / 2;
31385 imageContext.translate(center, center);
31387 imageContext.rotate(this.rotate * Math.PI / 180);
31389 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31391 var canvas = document.createElement("canvas");
31393 var context = canvas.getContext("2d");
31395 canvas.width = this.minWidth;
31396 canvas.height = this.minHeight;
31398 switch (this.rotate) {
31401 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31402 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31404 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31405 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31407 var targetWidth = this.minWidth - 2 * x;
31408 var targetHeight = this.minHeight - 2 * y;
31412 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31413 scale = targetWidth / width;
31416 if(x > 0 && y == 0){
31417 scale = targetHeight / height;
31420 if(x > 0 && y > 0){
31421 scale = targetWidth / width;
31423 if(width < height){
31424 scale = targetHeight / height;
31428 context.scale(scale, scale);
31430 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31431 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31433 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31434 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31436 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31441 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31442 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31444 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31445 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31447 var targetWidth = this.minWidth - 2 * x;
31448 var targetHeight = this.minHeight - 2 * y;
31452 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31453 scale = targetWidth / width;
31456 if(x > 0 && y == 0){
31457 scale = targetHeight / height;
31460 if(x > 0 && y > 0){
31461 scale = targetWidth / width;
31463 if(width < height){
31464 scale = targetHeight / height;
31468 context.scale(scale, scale);
31470 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31471 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31473 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31474 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31476 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31478 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31483 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31484 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31486 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31487 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31489 var targetWidth = this.minWidth - 2 * x;
31490 var targetHeight = this.minHeight - 2 * y;
31494 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31495 scale = targetWidth / width;
31498 if(x > 0 && y == 0){
31499 scale = targetHeight / height;
31502 if(x > 0 && y > 0){
31503 scale = targetWidth / width;
31505 if(width < height){
31506 scale = targetHeight / height;
31510 context.scale(scale, scale);
31512 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31513 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31515 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31516 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31518 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31519 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31521 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31526 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31527 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31529 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31530 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31532 var targetWidth = this.minWidth - 2 * x;
31533 var targetHeight = this.minHeight - 2 * y;
31537 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31538 scale = targetWidth / width;
31541 if(x > 0 && y == 0){
31542 scale = targetHeight / height;
31545 if(x > 0 && y > 0){
31546 scale = targetWidth / width;
31548 if(width < height){
31549 scale = targetHeight / height;
31553 context.scale(scale, scale);
31555 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31556 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31558 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31559 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31561 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31563 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31570 this.cropData = canvas.toDataURL(this.cropType);
31572 if(this.fireEvent('crop', this, this.cropData) !== false){
31573 this.process(this.file, this.cropData);
31580 setThumbBoxSize : function()
31584 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31585 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31586 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31588 this.minWidth = width;
31589 this.minHeight = height;
31591 if(this.rotate == 90 || this.rotate == 270){
31592 this.minWidth = height;
31593 this.minHeight = width;
31598 width = Math.ceil(this.minWidth * height / this.minHeight);
31600 if(this.minWidth > this.minHeight){
31602 height = Math.ceil(this.minHeight * width / this.minWidth);
31605 this.thumbEl.setStyle({
31606 width : width + 'px',
31607 height : height + 'px'
31614 setThumbBoxPosition : function()
31616 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31617 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31619 this.thumbEl.setLeft(x);
31620 this.thumbEl.setTop(y);
31624 baseRotateLevel : function()
31626 this.baseRotate = 1;
31629 typeof(this.exif) != 'undefined' &&
31630 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31631 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31633 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31636 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31640 baseScaleLevel : function()
31644 if(this.isDocument){
31646 if(this.baseRotate == 6 || this.baseRotate == 8){
31648 height = this.thumbEl.getHeight();
31649 this.baseScale = height / this.imageEl.OriginWidth;
31651 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31652 width = this.thumbEl.getWidth();
31653 this.baseScale = width / this.imageEl.OriginHeight;
31659 height = this.thumbEl.getHeight();
31660 this.baseScale = height / this.imageEl.OriginHeight;
31662 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31663 width = this.thumbEl.getWidth();
31664 this.baseScale = width / this.imageEl.OriginWidth;
31670 if(this.baseRotate == 6 || this.baseRotate == 8){
31672 width = this.thumbEl.getHeight();
31673 this.baseScale = width / this.imageEl.OriginHeight;
31675 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31676 height = this.thumbEl.getWidth();
31677 this.baseScale = height / this.imageEl.OriginHeight;
31680 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31681 height = this.thumbEl.getWidth();
31682 this.baseScale = height / this.imageEl.OriginHeight;
31684 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31685 width = this.thumbEl.getHeight();
31686 this.baseScale = width / this.imageEl.OriginWidth;
31693 width = this.thumbEl.getWidth();
31694 this.baseScale = width / this.imageEl.OriginWidth;
31696 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31697 height = this.thumbEl.getHeight();
31698 this.baseScale = height / this.imageEl.OriginHeight;
31701 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31703 height = this.thumbEl.getHeight();
31704 this.baseScale = height / this.imageEl.OriginHeight;
31706 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31707 width = this.thumbEl.getWidth();
31708 this.baseScale = width / this.imageEl.OriginWidth;
31716 getScaleLevel : function()
31718 return this.baseScale * Math.pow(1.1, this.scale);
31721 onTouchStart : function(e)
31723 if(!this.canvasLoaded){
31724 this.beforeSelectFile(e);
31728 var touches = e.browserEvent.touches;
31734 if(touches.length == 1){
31735 this.onMouseDown(e);
31739 if(touches.length != 2){
31745 for(var i = 0, finger; finger = touches[i]; i++){
31746 coords.push(finger.pageX, finger.pageY);
31749 var x = Math.pow(coords[0] - coords[2], 2);
31750 var y = Math.pow(coords[1] - coords[3], 2);
31752 this.startDistance = Math.sqrt(x + y);
31754 this.startScale = this.scale;
31756 this.pinching = true;
31757 this.dragable = false;
31761 onTouchMove : function(e)
31763 if(!this.pinching && !this.dragable){
31767 var touches = e.browserEvent.touches;
31774 this.onMouseMove(e);
31780 for(var i = 0, finger; finger = touches[i]; i++){
31781 coords.push(finger.pageX, finger.pageY);
31784 var x = Math.pow(coords[0] - coords[2], 2);
31785 var y = Math.pow(coords[1] - coords[3], 2);
31787 this.endDistance = Math.sqrt(x + y);
31789 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31791 if(!this.zoomable()){
31792 this.scale = this.startScale;
31800 onTouchEnd : function(e)
31802 this.pinching = false;
31803 this.dragable = false;
31807 process : function(file, crop)
31810 this.maskEl.mask(this.loadingText);
31813 this.xhr = new XMLHttpRequest();
31815 file.xhr = this.xhr;
31817 this.xhr.open(this.method, this.url, true);
31820 "Accept": "application/json",
31821 "Cache-Control": "no-cache",
31822 "X-Requested-With": "XMLHttpRequest"
31825 for (var headerName in headers) {
31826 var headerValue = headers[headerName];
31828 this.xhr.setRequestHeader(headerName, headerValue);
31834 this.xhr.onload = function()
31836 _this.xhrOnLoad(_this.xhr);
31839 this.xhr.onerror = function()
31841 _this.xhrOnError(_this.xhr);
31844 var formData = new FormData();
31846 formData.append('returnHTML', 'NO');
31849 formData.append('crop', crop);
31852 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31853 formData.append(this.paramName, file, file.name);
31856 if(typeof(file.filename) != 'undefined'){
31857 formData.append('filename', file.filename);
31860 if(typeof(file.mimetype) != 'undefined'){
31861 formData.append('mimetype', file.mimetype);
31864 if(this.fireEvent('arrange', this, formData) != false){
31865 this.xhr.send(formData);
31869 xhrOnLoad : function(xhr)
31872 this.maskEl.unmask();
31875 if (xhr.readyState !== 4) {
31876 this.fireEvent('exception', this, xhr);
31880 var response = Roo.decode(xhr.responseText);
31882 if(!response.success){
31883 this.fireEvent('exception', this, xhr);
31887 var response = Roo.decode(xhr.responseText);
31889 this.fireEvent('upload', this, response);
31893 xhrOnError : function()
31896 this.maskEl.unmask();
31899 Roo.log('xhr on error');
31901 var response = Roo.decode(xhr.responseText);
31907 prepare : function(file)
31910 this.maskEl.mask(this.loadingText);
31916 if(typeof(file) === 'string'){
31917 this.loadCanvas(file);
31921 if(!file || !this.urlAPI){
31926 this.cropType = file.type;
31930 if(this.fireEvent('prepare', this, this.file) != false){
31932 var reader = new FileReader();
31934 reader.onload = function (e) {
31935 if (e.target.error) {
31936 Roo.log(e.target.error);
31940 var buffer = e.target.result,
31941 dataView = new DataView(buffer),
31943 maxOffset = dataView.byteLength - 4,
31947 if (dataView.getUint16(0) === 0xffd8) {
31948 while (offset < maxOffset) {
31949 markerBytes = dataView.getUint16(offset);
31951 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31952 markerLength = dataView.getUint16(offset + 2) + 2;
31953 if (offset + markerLength > dataView.byteLength) {
31954 Roo.log('Invalid meta data: Invalid segment size.');
31958 if(markerBytes == 0xffe1){
31959 _this.parseExifData(
31966 offset += markerLength;
31976 var url = _this.urlAPI.createObjectURL(_this.file);
31978 _this.loadCanvas(url);
31983 reader.readAsArrayBuffer(this.file);
31989 parseExifData : function(dataView, offset, length)
31991 var tiffOffset = offset + 10,
31995 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31996 // No Exif data, might be XMP data instead
32000 // Check for the ASCII code for "Exif" (0x45786966):
32001 if (dataView.getUint32(offset + 4) !== 0x45786966) {
32002 // No Exif data, might be XMP data instead
32005 if (tiffOffset + 8 > dataView.byteLength) {
32006 Roo.log('Invalid Exif data: Invalid segment size.');
32009 // Check for the two null bytes:
32010 if (dataView.getUint16(offset + 8) !== 0x0000) {
32011 Roo.log('Invalid Exif data: Missing byte alignment offset.');
32014 // Check the byte alignment:
32015 switch (dataView.getUint16(tiffOffset)) {
32017 littleEndian = true;
32020 littleEndian = false;
32023 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32026 // Check for the TIFF tag marker (0x002A):
32027 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32028 Roo.log('Invalid Exif data: Missing TIFF marker.');
32031 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32032 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32034 this.parseExifTags(
32037 tiffOffset + dirOffset,
32042 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32047 if (dirOffset + 6 > dataView.byteLength) {
32048 Roo.log('Invalid Exif data: Invalid directory offset.');
32051 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32052 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32053 if (dirEndOffset + 4 > dataView.byteLength) {
32054 Roo.log('Invalid Exif data: Invalid directory size.');
32057 for (i = 0; i < tagsNumber; i += 1) {
32061 dirOffset + 2 + 12 * i, // tag offset
32065 // Return the offset to the next directory:
32066 return dataView.getUint32(dirEndOffset, littleEndian);
32069 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32071 var tag = dataView.getUint16(offset, littleEndian);
32073 this.exif[tag] = this.getExifValue(
32077 dataView.getUint16(offset + 2, littleEndian), // tag type
32078 dataView.getUint32(offset + 4, littleEndian), // tag length
32083 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32085 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32094 Roo.log('Invalid Exif data: Invalid tag type.');
32098 tagSize = tagType.size * length;
32099 // Determine if the value is contained in the dataOffset bytes,
32100 // or if the value at the dataOffset is a pointer to the actual data:
32101 dataOffset = tagSize > 4 ?
32102 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32103 if (dataOffset + tagSize > dataView.byteLength) {
32104 Roo.log('Invalid Exif data: Invalid data offset.');
32107 if (length === 1) {
32108 return tagType.getValue(dataView, dataOffset, littleEndian);
32111 for (i = 0; i < length; i += 1) {
32112 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32115 if (tagType.ascii) {
32117 // Concatenate the chars:
32118 for (i = 0; i < values.length; i += 1) {
32120 // Ignore the terminating NULL byte(s):
32121 if (c === '\u0000') {
32133 Roo.apply(Roo.bootstrap.UploadCropbox, {
32135 'Orientation': 0x0112
32139 1: 0, //'top-left',
32141 3: 180, //'bottom-right',
32142 // 4: 'bottom-left',
32144 6: 90, //'right-top',
32145 // 7: 'right-bottom',
32146 8: 270 //'left-bottom'
32150 // byte, 8-bit unsigned int:
32152 getValue: function (dataView, dataOffset) {
32153 return dataView.getUint8(dataOffset);
32157 // ascii, 8-bit byte:
32159 getValue: function (dataView, dataOffset) {
32160 return String.fromCharCode(dataView.getUint8(dataOffset));
32165 // short, 16 bit int:
32167 getValue: function (dataView, dataOffset, littleEndian) {
32168 return dataView.getUint16(dataOffset, littleEndian);
32172 // long, 32 bit int:
32174 getValue: function (dataView, dataOffset, littleEndian) {
32175 return dataView.getUint32(dataOffset, littleEndian);
32179 // rational = two long values, first is numerator, second is denominator:
32181 getValue: function (dataView, dataOffset, littleEndian) {
32182 return dataView.getUint32(dataOffset, littleEndian) /
32183 dataView.getUint32(dataOffset + 4, littleEndian);
32187 // slong, 32 bit signed int:
32189 getValue: function (dataView, dataOffset, littleEndian) {
32190 return dataView.getInt32(dataOffset, littleEndian);
32194 // srational, two slongs, first is numerator, second is denominator:
32196 getValue: function (dataView, dataOffset, littleEndian) {
32197 return dataView.getInt32(dataOffset, littleEndian) /
32198 dataView.getInt32(dataOffset + 4, littleEndian);
32208 cls : 'btn-group roo-upload-cropbox-rotate-left',
32209 action : 'rotate-left',
32213 cls : 'btn btn-default',
32214 html : '<i class="fa fa-undo"></i>'
32220 cls : 'btn-group roo-upload-cropbox-picture',
32221 action : 'picture',
32225 cls : 'btn btn-default',
32226 html : '<i class="fa fa-picture-o"></i>'
32232 cls : 'btn-group roo-upload-cropbox-rotate-right',
32233 action : 'rotate-right',
32237 cls : 'btn btn-default',
32238 html : '<i class="fa fa-repeat"></i>'
32246 cls : 'btn-group roo-upload-cropbox-rotate-left',
32247 action : 'rotate-left',
32251 cls : 'btn btn-default',
32252 html : '<i class="fa fa-undo"></i>'
32258 cls : 'btn-group roo-upload-cropbox-download',
32259 action : 'download',
32263 cls : 'btn btn-default',
32264 html : '<i class="fa fa-download"></i>'
32270 cls : 'btn-group roo-upload-cropbox-crop',
32275 cls : 'btn btn-default',
32276 html : '<i class="fa fa-crop"></i>'
32282 cls : 'btn-group roo-upload-cropbox-trash',
32287 cls : 'btn btn-default',
32288 html : '<i class="fa fa-trash"></i>'
32294 cls : 'btn-group roo-upload-cropbox-rotate-right',
32295 action : 'rotate-right',
32299 cls : 'btn btn-default',
32300 html : '<i class="fa fa-repeat"></i>'
32308 cls : 'btn-group roo-upload-cropbox-rotate-left',
32309 action : 'rotate-left',
32313 cls : 'btn btn-default',
32314 html : '<i class="fa fa-undo"></i>'
32320 cls : 'btn-group roo-upload-cropbox-rotate-right',
32321 action : 'rotate-right',
32325 cls : 'btn btn-default',
32326 html : '<i class="fa fa-repeat"></i>'
32339 * @class Roo.bootstrap.DocumentManager
32340 * @extends Roo.bootstrap.Component
32341 * Bootstrap DocumentManager class
32342 * @cfg {String} paramName default 'imageUpload'
32343 * @cfg {String} toolTipName default 'filename'
32344 * @cfg {String} method default POST
32345 * @cfg {String} url action url
32346 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32347 * @cfg {Boolean} multiple multiple upload default true
32348 * @cfg {Number} thumbSize default 300
32349 * @cfg {String} fieldLabel
32350 * @cfg {Number} labelWidth default 4
32351 * @cfg {String} labelAlign (left|top) default left
32352 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32353 * @cfg {Number} labellg set the width of label (1-12)
32354 * @cfg {Number} labelmd set the width of label (1-12)
32355 * @cfg {Number} labelsm set the width of label (1-12)
32356 * @cfg {Number} labelxs set the width of label (1-12)
32359 * Create a new DocumentManager
32360 * @param {Object} config The config object
32363 Roo.bootstrap.DocumentManager = function(config){
32364 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32367 this.delegates = [];
32372 * Fire when initial the DocumentManager
32373 * @param {Roo.bootstrap.DocumentManager} this
32378 * inspect selected file
32379 * @param {Roo.bootstrap.DocumentManager} this
32380 * @param {File} file
32385 * Fire when xhr load exception
32386 * @param {Roo.bootstrap.DocumentManager} this
32387 * @param {XMLHttpRequest} xhr
32389 "exception" : true,
32391 * @event afterupload
32392 * Fire when xhr load exception
32393 * @param {Roo.bootstrap.DocumentManager} this
32394 * @param {XMLHttpRequest} xhr
32396 "afterupload" : true,
32399 * prepare the form data
32400 * @param {Roo.bootstrap.DocumentManager} this
32401 * @param {Object} formData
32406 * Fire when remove the file
32407 * @param {Roo.bootstrap.DocumentManager} this
32408 * @param {Object} file
32413 * Fire after refresh the file
32414 * @param {Roo.bootstrap.DocumentManager} this
32419 * Fire after click the image
32420 * @param {Roo.bootstrap.DocumentManager} this
32421 * @param {Object} file
32426 * Fire when upload a image and editable set to true
32427 * @param {Roo.bootstrap.DocumentManager} this
32428 * @param {Object} file
32432 * @event beforeselectfile
32433 * Fire before select file
32434 * @param {Roo.bootstrap.DocumentManager} this
32436 "beforeselectfile" : true,
32439 * Fire before process file
32440 * @param {Roo.bootstrap.DocumentManager} this
32441 * @param {Object} file
32445 * @event previewrendered
32446 * Fire when preview rendered
32447 * @param {Roo.bootstrap.DocumentManager} this
32448 * @param {Object} file
32450 "previewrendered" : true,
32453 "previewResize" : true
32458 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32467 paramName : 'imageUpload',
32468 toolTipName : 'filename',
32471 labelAlign : 'left',
32481 getAutoCreate : function()
32483 var managerWidget = {
32485 cls : 'roo-document-manager',
32489 cls : 'roo-document-manager-selector',
32494 cls : 'roo-document-manager-uploader',
32498 cls : 'roo-document-manager-upload-btn',
32499 html : '<i class="fa fa-plus"></i>'
32510 cls : 'column col-md-12',
32515 if(this.fieldLabel.length){
32520 cls : 'column col-md-12',
32521 html : this.fieldLabel
32525 cls : 'column col-md-12',
32530 if(this.labelAlign == 'left'){
32535 html : this.fieldLabel
32544 if(this.labelWidth > 12){
32545 content[0].style = "width: " + this.labelWidth + 'px';
32548 if(this.labelWidth < 13 && this.labelmd == 0){
32549 this.labelmd = this.labelWidth;
32552 if(this.labellg > 0){
32553 content[0].cls += ' col-lg-' + this.labellg;
32554 content[1].cls += ' col-lg-' + (12 - this.labellg);
32557 if(this.labelmd > 0){
32558 content[0].cls += ' col-md-' + this.labelmd;
32559 content[1].cls += ' col-md-' + (12 - this.labelmd);
32562 if(this.labelsm > 0){
32563 content[0].cls += ' col-sm-' + this.labelsm;
32564 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32567 if(this.labelxs > 0){
32568 content[0].cls += ' col-xs-' + this.labelxs;
32569 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32577 cls : 'row clearfix',
32585 initEvents : function()
32587 this.managerEl = this.el.select('.roo-document-manager', true).first();
32588 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32590 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32591 this.selectorEl.hide();
32594 this.selectorEl.attr('multiple', 'multiple');
32597 this.selectorEl.on('change', this.onFileSelected, this);
32599 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32600 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32602 this.uploader.on('click', this.onUploaderClick, this);
32604 this.renderProgressDialog();
32608 window.addEventListener("resize", function() { _this.refresh(); } );
32610 this.fireEvent('initial', this);
32613 renderProgressDialog : function()
32617 this.progressDialog = new Roo.bootstrap.Modal({
32618 cls : 'roo-document-manager-progress-dialog',
32619 allow_close : false,
32630 btnclick : function() {
32631 _this.uploadCancel();
32637 this.progressDialog.render(Roo.get(document.body));
32639 this.progress = new Roo.bootstrap.Progress({
32640 cls : 'roo-document-manager-progress',
32645 this.progress.render(this.progressDialog.getChildContainer());
32647 this.progressBar = new Roo.bootstrap.ProgressBar({
32648 cls : 'roo-document-manager-progress-bar',
32651 aria_valuemax : 12,
32655 this.progressBar.render(this.progress.getChildContainer());
32658 onUploaderClick : function(e)
32660 e.preventDefault();
32662 if(this.fireEvent('beforeselectfile', this) != false){
32663 this.selectorEl.dom.click();
32668 onFileSelected : function(e)
32670 e.preventDefault();
32672 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32676 Roo.each(this.selectorEl.dom.files, function(file){
32677 if(this.fireEvent('inspect', this, file) != false){
32678 this.files.push(file);
32688 this.selectorEl.dom.value = '';
32690 if(!this.files || !this.files.length){
32694 if(this.boxes > 0 && this.files.length > this.boxes){
32695 this.files = this.files.slice(0, this.boxes);
32698 this.uploader.show();
32700 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32701 this.uploader.hide();
32710 Roo.each(this.files, function(file){
32712 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32713 var f = this.renderPreview(file);
32718 if(file.type.indexOf('image') != -1){
32719 this.delegates.push(
32721 _this.process(file);
32722 }).createDelegate(this)
32730 _this.process(file);
32731 }).createDelegate(this)
32736 this.files = files;
32738 this.delegates = this.delegates.concat(docs);
32740 if(!this.delegates.length){
32745 this.progressBar.aria_valuemax = this.delegates.length;
32752 arrange : function()
32754 if(!this.delegates.length){
32755 this.progressDialog.hide();
32760 var delegate = this.delegates.shift();
32762 this.progressDialog.show();
32764 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32766 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32771 refresh : function()
32773 this.uploader.show();
32775 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32776 this.uploader.hide();
32779 Roo.isTouch ? this.closable(false) : this.closable(true);
32781 this.fireEvent('refresh', this);
32784 onRemove : function(e, el, o)
32786 e.preventDefault();
32788 this.fireEvent('remove', this, o);
32792 remove : function(o)
32796 Roo.each(this.files, function(file){
32797 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32806 this.files = files;
32813 Roo.each(this.files, function(file){
32818 file.target.remove();
32827 onClick : function(e, el, o)
32829 e.preventDefault();
32831 this.fireEvent('click', this, o);
32835 closable : function(closable)
32837 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32839 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32851 xhrOnLoad : function(xhr)
32853 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32857 if (xhr.readyState !== 4) {
32859 this.fireEvent('exception', this, xhr);
32863 var response = Roo.decode(xhr.responseText);
32865 if(!response.success){
32867 this.fireEvent('exception', this, xhr);
32871 var file = this.renderPreview(response.data);
32873 this.files.push(file);
32877 this.fireEvent('afterupload', this, xhr);
32881 xhrOnError : function(xhr)
32883 Roo.log('xhr on error');
32885 var response = Roo.decode(xhr.responseText);
32892 process : function(file)
32894 if(this.fireEvent('process', this, file) !== false){
32895 if(this.editable && file.type.indexOf('image') != -1){
32896 this.fireEvent('edit', this, file);
32900 this.uploadStart(file, false);
32907 uploadStart : function(file, crop)
32909 this.xhr = new XMLHttpRequest();
32911 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32916 file.xhr = this.xhr;
32918 this.managerEl.createChild({
32920 cls : 'roo-document-manager-loading',
32924 tooltip : file.name,
32925 cls : 'roo-document-manager-thumb',
32926 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32932 this.xhr.open(this.method, this.url, true);
32935 "Accept": "application/json",
32936 "Cache-Control": "no-cache",
32937 "X-Requested-With": "XMLHttpRequest"
32940 for (var headerName in headers) {
32941 var headerValue = headers[headerName];
32943 this.xhr.setRequestHeader(headerName, headerValue);
32949 this.xhr.onload = function()
32951 _this.xhrOnLoad(_this.xhr);
32954 this.xhr.onerror = function()
32956 _this.xhrOnError(_this.xhr);
32959 var formData = new FormData();
32961 formData.append('returnHTML', 'NO');
32964 formData.append('crop', crop);
32967 formData.append(this.paramName, file, file.name);
32974 if(this.fireEvent('prepare', this, formData, options) != false){
32976 if(options.manually){
32980 this.xhr.send(formData);
32984 this.uploadCancel();
32987 uploadCancel : function()
32993 this.delegates = [];
32995 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33002 renderPreview : function(file)
33004 if(typeof(file.target) != 'undefined' && file.target){
33008 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33010 var previewEl = this.managerEl.createChild({
33012 cls : 'roo-document-manager-preview',
33016 tooltip : file[this.toolTipName],
33017 cls : 'roo-document-manager-thumb',
33018 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33023 html : '<i class="fa fa-times-circle"></i>'
33028 var close = previewEl.select('button.close', true).first();
33030 close.on('click', this.onRemove, this, file);
33032 file.target = previewEl;
33034 var image = previewEl.select('img', true).first();
33038 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33040 image.on('click', this.onClick, this, file);
33042 this.fireEvent('previewrendered', this, file);
33048 onPreviewLoad : function(file, image)
33050 if(typeof(file.target) == 'undefined' || !file.target){
33054 var width = image.dom.naturalWidth || image.dom.width;
33055 var height = image.dom.naturalHeight || image.dom.height;
33057 if(!this.previewResize) {
33061 if(width > height){
33062 file.target.addClass('wide');
33066 file.target.addClass('tall');
33071 uploadFromSource : function(file, crop)
33073 this.xhr = new XMLHttpRequest();
33075 this.managerEl.createChild({
33077 cls : 'roo-document-manager-loading',
33081 tooltip : file.name,
33082 cls : 'roo-document-manager-thumb',
33083 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33089 this.xhr.open(this.method, this.url, true);
33092 "Accept": "application/json",
33093 "Cache-Control": "no-cache",
33094 "X-Requested-With": "XMLHttpRequest"
33097 for (var headerName in headers) {
33098 var headerValue = headers[headerName];
33100 this.xhr.setRequestHeader(headerName, headerValue);
33106 this.xhr.onload = function()
33108 _this.xhrOnLoad(_this.xhr);
33111 this.xhr.onerror = function()
33113 _this.xhrOnError(_this.xhr);
33116 var formData = new FormData();
33118 formData.append('returnHTML', 'NO');
33120 formData.append('crop', crop);
33122 if(typeof(file.filename) != 'undefined'){
33123 formData.append('filename', file.filename);
33126 if(typeof(file.mimetype) != 'undefined'){
33127 formData.append('mimetype', file.mimetype);
33132 if(this.fireEvent('prepare', this, formData) != false){
33133 this.xhr.send(formData);
33143 * @class Roo.bootstrap.DocumentViewer
33144 * @extends Roo.bootstrap.Component
33145 * Bootstrap DocumentViewer class
33146 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33147 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33150 * Create a new DocumentViewer
33151 * @param {Object} config The config object
33154 Roo.bootstrap.DocumentViewer = function(config){
33155 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33160 * Fire after initEvent
33161 * @param {Roo.bootstrap.DocumentViewer} this
33167 * @param {Roo.bootstrap.DocumentViewer} this
33172 * Fire after download button
33173 * @param {Roo.bootstrap.DocumentViewer} this
33178 * Fire after trash button
33179 * @param {Roo.bootstrap.DocumentViewer} this
33186 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33188 showDownload : true,
33192 getAutoCreate : function()
33196 cls : 'roo-document-viewer',
33200 cls : 'roo-document-viewer-body',
33204 cls : 'roo-document-viewer-thumb',
33208 cls : 'roo-document-viewer-image'
33216 cls : 'roo-document-viewer-footer',
33219 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33223 cls : 'btn-group roo-document-viewer-download',
33227 cls : 'btn btn-default',
33228 html : '<i class="fa fa-download"></i>'
33234 cls : 'btn-group roo-document-viewer-trash',
33238 cls : 'btn btn-default',
33239 html : '<i class="fa fa-trash"></i>'
33252 initEvents : function()
33254 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33255 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33257 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33258 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33260 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33261 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33263 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33264 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33266 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33267 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33269 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33270 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33272 this.bodyEl.on('click', this.onClick, this);
33273 this.downloadBtn.on('click', this.onDownload, this);
33274 this.trashBtn.on('click', this.onTrash, this);
33276 this.downloadBtn.hide();
33277 this.trashBtn.hide();
33279 if(this.showDownload){
33280 this.downloadBtn.show();
33283 if(this.showTrash){
33284 this.trashBtn.show();
33287 if(!this.showDownload && !this.showTrash) {
33288 this.footerEl.hide();
33293 initial : function()
33295 this.fireEvent('initial', this);
33299 onClick : function(e)
33301 e.preventDefault();
33303 this.fireEvent('click', this);
33306 onDownload : function(e)
33308 e.preventDefault();
33310 this.fireEvent('download', this);
33313 onTrash : function(e)
33315 e.preventDefault();
33317 this.fireEvent('trash', this);
33329 * @class Roo.bootstrap.NavProgressBar
33330 * @extends Roo.bootstrap.Component
33331 * Bootstrap NavProgressBar class
33334 * Create a new nav progress bar
33335 * @param {Object} config The config object
33338 Roo.bootstrap.NavProgressBar = function(config){
33339 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33341 this.bullets = this.bullets || [];
33343 // Roo.bootstrap.NavProgressBar.register(this);
33347 * Fires when the active item changes
33348 * @param {Roo.bootstrap.NavProgressBar} this
33349 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33350 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
33357 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
33362 getAutoCreate : function()
33364 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33368 cls : 'roo-navigation-bar-group',
33372 cls : 'roo-navigation-top-bar'
33376 cls : 'roo-navigation-bullets-bar',
33380 cls : 'roo-navigation-bar'
33387 cls : 'roo-navigation-bottom-bar'
33397 initEvents: function()
33402 onRender : function(ct, position)
33404 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33406 if(this.bullets.length){
33407 Roo.each(this.bullets, function(b){
33416 addItem : function(cfg)
33418 var item = new Roo.bootstrap.NavProgressItem(cfg);
33420 item.parentId = this.id;
33421 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33424 var top = new Roo.bootstrap.Element({
33426 cls : 'roo-navigation-bar-text'
33429 var bottom = new Roo.bootstrap.Element({
33431 cls : 'roo-navigation-bar-text'
33434 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33435 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33437 var topText = new Roo.bootstrap.Element({
33439 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33442 var bottomText = new Roo.bootstrap.Element({
33444 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33447 topText.onRender(top.el, null);
33448 bottomText.onRender(bottom.el, null);
33451 item.bottomEl = bottom;
33454 this.barItems.push(item);
33459 getActive : function()
33461 var active = false;
33463 Roo.each(this.barItems, function(v){
33465 if (!v.isActive()) {
33477 setActiveItem : function(item)
33481 Roo.each(this.barItems, function(v){
33482 if (v.rid == item.rid) {
33486 if (v.isActive()) {
33487 v.setActive(false);
33492 item.setActive(true);
33494 this.fireEvent('changed', this, item, prev);
33497 getBarItem: function(rid)
33501 Roo.each(this.barItems, function(e) {
33502 if (e.rid != rid) {
33513 indexOfItem : function(item)
33517 Roo.each(this.barItems, function(v, i){
33519 if (v.rid != item.rid) {
33530 setActiveNext : function()
33532 var i = this.indexOfItem(this.getActive());
33534 if (i > this.barItems.length) {
33538 this.setActiveItem(this.barItems[i+1]);
33541 setActivePrev : function()
33543 var i = this.indexOfItem(this.getActive());
33549 this.setActiveItem(this.barItems[i-1]);
33552 format : function()
33554 if(!this.barItems.length){
33558 var width = 100 / this.barItems.length;
33560 Roo.each(this.barItems, function(i){
33561 i.el.setStyle('width', width + '%');
33562 i.topEl.el.setStyle('width', width + '%');
33563 i.bottomEl.el.setStyle('width', width + '%');
33572 * Nav Progress Item
33577 * @class Roo.bootstrap.NavProgressItem
33578 * @extends Roo.bootstrap.Component
33579 * Bootstrap NavProgressItem class
33580 * @cfg {String} rid the reference id
33581 * @cfg {Boolean} active (true|false) Is item active default false
33582 * @cfg {Boolean} disabled (true|false) Is item active default false
33583 * @cfg {String} html
33584 * @cfg {String} position (top|bottom) text position default bottom
33585 * @cfg {String} icon show icon instead of number
33588 * Create a new NavProgressItem
33589 * @param {Object} config The config object
33591 Roo.bootstrap.NavProgressItem = function(config){
33592 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33597 * The raw click event for the entire grid.
33598 * @param {Roo.bootstrap.NavProgressItem} this
33599 * @param {Roo.EventObject} e
33606 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33612 position : 'bottom',
33615 getAutoCreate : function()
33617 var iconCls = 'roo-navigation-bar-item-icon';
33619 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33623 cls: 'roo-navigation-bar-item',
33633 cfg.cls += ' active';
33636 cfg.cls += ' disabled';
33642 disable : function()
33644 this.setDisabled(true);
33647 enable : function()
33649 this.setDisabled(false);
33652 initEvents: function()
33654 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33656 this.iconEl.on('click', this.onClick, this);
33659 onClick : function(e)
33661 e.preventDefault();
33667 if(this.fireEvent('click', this, e) === false){
33671 this.parent().setActiveItem(this);
33674 isActive: function ()
33676 return this.active;
33679 setActive : function(state)
33681 if(this.active == state){
33685 this.active = state;
33688 this.el.addClass('active');
33692 this.el.removeClass('active');
33697 setDisabled : function(state)
33699 if(this.disabled == state){
33703 this.disabled = state;
33706 this.el.addClass('disabled');
33710 this.el.removeClass('disabled');
33713 tooltipEl : function()
33715 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33728 * @class Roo.bootstrap.FieldLabel
33729 * @extends Roo.bootstrap.Component
33730 * Bootstrap FieldLabel class
33731 * @cfg {String} html contents of the element
33732 * @cfg {String} tag tag of the element default label
33733 * @cfg {String} cls class of the element
33734 * @cfg {String} target label target
33735 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33736 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33737 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33738 * @cfg {String} iconTooltip default "This field is required"
33739 * @cfg {String} indicatorpos (left|right) default left
33742 * Create a new FieldLabel
33743 * @param {Object} config The config object
33746 Roo.bootstrap.FieldLabel = function(config){
33747 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33752 * Fires after the field has been marked as invalid.
33753 * @param {Roo.form.FieldLabel} this
33754 * @param {String} msg The validation message
33759 * Fires after the field has been validated with no errors.
33760 * @param {Roo.form.FieldLabel} this
33766 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33773 invalidClass : 'has-warning',
33774 validClass : 'has-success',
33775 iconTooltip : 'This field is required',
33776 indicatorpos : 'left',
33778 getAutoCreate : function(){
33781 if (!this.allowBlank) {
33787 cls : 'roo-bootstrap-field-label ' + this.cls,
33792 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33793 tooltip : this.iconTooltip
33802 if(this.indicatorpos == 'right'){
33805 cls : 'roo-bootstrap-field-label ' + this.cls,
33814 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33815 tooltip : this.iconTooltip
33824 initEvents: function()
33826 Roo.bootstrap.Element.superclass.initEvents.call(this);
33828 this.indicator = this.indicatorEl();
33830 if(this.indicator){
33831 this.indicator.removeClass('visible');
33832 this.indicator.addClass('invisible');
33835 Roo.bootstrap.FieldLabel.register(this);
33838 indicatorEl : function()
33840 var indicator = this.el.select('i.roo-required-indicator',true).first();
33851 * Mark this field as valid
33853 markValid : function()
33855 if(this.indicator){
33856 this.indicator.removeClass('visible');
33857 this.indicator.addClass('invisible');
33859 if (Roo.bootstrap.version == 3) {
33860 this.el.removeClass(this.invalidClass);
33861 this.el.addClass(this.validClass);
33863 this.el.removeClass('is-invalid');
33864 this.el.addClass('is-valid');
33868 this.fireEvent('valid', this);
33872 * Mark this field as invalid
33873 * @param {String} msg The validation message
33875 markInvalid : function(msg)
33877 if(this.indicator){
33878 this.indicator.removeClass('invisible');
33879 this.indicator.addClass('visible');
33881 if (Roo.bootstrap.version == 3) {
33882 this.el.removeClass(this.validClass);
33883 this.el.addClass(this.invalidClass);
33885 this.el.removeClass('is-valid');
33886 this.el.addClass('is-invalid');
33890 this.fireEvent('invalid', this, msg);
33896 Roo.apply(Roo.bootstrap.FieldLabel, {
33901 * register a FieldLabel Group
33902 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33904 register : function(label)
33906 if(this.groups.hasOwnProperty(label.target)){
33910 this.groups[label.target] = label;
33914 * fetch a FieldLabel Group based on the target
33915 * @param {string} target
33916 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33918 get: function(target) {
33919 if (typeof(this.groups[target]) == 'undefined') {
33923 return this.groups[target] ;
33932 * page DateSplitField.
33938 * @class Roo.bootstrap.DateSplitField
33939 * @extends Roo.bootstrap.Component
33940 * Bootstrap DateSplitField class
33941 * @cfg {string} fieldLabel - the label associated
33942 * @cfg {Number} labelWidth set the width of label (0-12)
33943 * @cfg {String} labelAlign (top|left)
33944 * @cfg {Boolean} dayAllowBlank (true|false) default false
33945 * @cfg {Boolean} monthAllowBlank (true|false) default false
33946 * @cfg {Boolean} yearAllowBlank (true|false) default false
33947 * @cfg {string} dayPlaceholder
33948 * @cfg {string} monthPlaceholder
33949 * @cfg {string} yearPlaceholder
33950 * @cfg {string} dayFormat default 'd'
33951 * @cfg {string} monthFormat default 'm'
33952 * @cfg {string} yearFormat default 'Y'
33953 * @cfg {Number} labellg set the width of label (1-12)
33954 * @cfg {Number} labelmd set the width of label (1-12)
33955 * @cfg {Number} labelsm set the width of label (1-12)
33956 * @cfg {Number} labelxs set the width of label (1-12)
33960 * Create a new DateSplitField
33961 * @param {Object} config The config object
33964 Roo.bootstrap.DateSplitField = function(config){
33965 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33971 * getting the data of years
33972 * @param {Roo.bootstrap.DateSplitField} this
33973 * @param {Object} years
33978 * getting the data of days
33979 * @param {Roo.bootstrap.DateSplitField} this
33980 * @param {Object} days
33985 * Fires after the field has been marked as invalid.
33986 * @param {Roo.form.Field} this
33987 * @param {String} msg The validation message
33992 * Fires after the field has been validated with no errors.
33993 * @param {Roo.form.Field} this
33999 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
34002 labelAlign : 'top',
34004 dayAllowBlank : false,
34005 monthAllowBlank : false,
34006 yearAllowBlank : false,
34007 dayPlaceholder : '',
34008 monthPlaceholder : '',
34009 yearPlaceholder : '',
34013 isFormField : true,
34019 getAutoCreate : function()
34023 cls : 'row roo-date-split-field-group',
34028 cls : 'form-hidden-field roo-date-split-field-group-value',
34034 var labelCls = 'col-md-12';
34035 var contentCls = 'col-md-4';
34037 if(this.fieldLabel){
34041 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34045 html : this.fieldLabel
34050 if(this.labelAlign == 'left'){
34052 if(this.labelWidth > 12){
34053 label.style = "width: " + this.labelWidth + 'px';
34056 if(this.labelWidth < 13 && this.labelmd == 0){
34057 this.labelmd = this.labelWidth;
34060 if(this.labellg > 0){
34061 labelCls = ' col-lg-' + this.labellg;
34062 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34065 if(this.labelmd > 0){
34066 labelCls = ' col-md-' + this.labelmd;
34067 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34070 if(this.labelsm > 0){
34071 labelCls = ' col-sm-' + this.labelsm;
34072 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34075 if(this.labelxs > 0){
34076 labelCls = ' col-xs-' + this.labelxs;
34077 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34081 label.cls += ' ' + labelCls;
34083 cfg.cn.push(label);
34086 Roo.each(['day', 'month', 'year'], function(t){
34089 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34096 inputEl: function ()
34098 return this.el.select('.roo-date-split-field-group-value', true).first();
34101 onRender : function(ct, position)
34105 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34107 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34109 this.dayField = new Roo.bootstrap.ComboBox({
34110 allowBlank : this.dayAllowBlank,
34111 alwaysQuery : true,
34112 displayField : 'value',
34115 forceSelection : true,
34117 placeholder : this.dayPlaceholder,
34118 selectOnFocus : true,
34119 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34120 triggerAction : 'all',
34122 valueField : 'value',
34123 store : new Roo.data.SimpleStore({
34124 data : (function() {
34126 _this.fireEvent('days', _this, days);
34129 fields : [ 'value' ]
34132 select : function (_self, record, index)
34134 _this.setValue(_this.getValue());
34139 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34141 this.monthField = new Roo.bootstrap.MonthField({
34142 after : '<i class=\"fa fa-calendar\"></i>',
34143 allowBlank : this.monthAllowBlank,
34144 placeholder : this.monthPlaceholder,
34147 render : function (_self)
34149 this.el.select('span.input-group-addon', true).first().on('click', function(e){
34150 e.preventDefault();
34154 select : function (_self, oldvalue, newvalue)
34156 _this.setValue(_this.getValue());
34161 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34163 this.yearField = new Roo.bootstrap.ComboBox({
34164 allowBlank : this.yearAllowBlank,
34165 alwaysQuery : true,
34166 displayField : 'value',
34169 forceSelection : true,
34171 placeholder : this.yearPlaceholder,
34172 selectOnFocus : true,
34173 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34174 triggerAction : 'all',
34176 valueField : 'value',
34177 store : new Roo.data.SimpleStore({
34178 data : (function() {
34180 _this.fireEvent('years', _this, years);
34183 fields : [ 'value' ]
34186 select : function (_self, record, index)
34188 _this.setValue(_this.getValue());
34193 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34196 setValue : function(v, format)
34198 this.inputEl.dom.value = v;
34200 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34202 var d = Date.parseDate(v, f);
34209 this.setDay(d.format(this.dayFormat));
34210 this.setMonth(d.format(this.monthFormat));
34211 this.setYear(d.format(this.yearFormat));
34218 setDay : function(v)
34220 this.dayField.setValue(v);
34221 this.inputEl.dom.value = this.getValue();
34226 setMonth : function(v)
34228 this.monthField.setValue(v, true);
34229 this.inputEl.dom.value = this.getValue();
34234 setYear : function(v)
34236 this.yearField.setValue(v);
34237 this.inputEl.dom.value = this.getValue();
34242 getDay : function()
34244 return this.dayField.getValue();
34247 getMonth : function()
34249 return this.monthField.getValue();
34252 getYear : function()
34254 return this.yearField.getValue();
34257 getValue : function()
34259 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34261 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34271 this.inputEl.dom.value = '';
34276 validate : function()
34278 var d = this.dayField.validate();
34279 var m = this.monthField.validate();
34280 var y = this.yearField.validate();
34285 (!this.dayAllowBlank && !d) ||
34286 (!this.monthAllowBlank && !m) ||
34287 (!this.yearAllowBlank && !y)
34292 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34301 this.markInvalid();
34306 markValid : function()
34309 var label = this.el.select('label', true).first();
34310 var icon = this.el.select('i.fa-star', true).first();
34316 this.fireEvent('valid', this);
34320 * Mark this field as invalid
34321 * @param {String} msg The validation message
34323 markInvalid : function(msg)
34326 var label = this.el.select('label', true).first();
34327 var icon = this.el.select('i.fa-star', true).first();
34329 if(label && !icon){
34330 this.el.select('.roo-date-split-field-label', true).createChild({
34332 cls : 'text-danger fa fa-lg fa-star',
34333 tooltip : 'This field is required',
34334 style : 'margin-right:5px;'
34338 this.fireEvent('invalid', this, msg);
34341 clearInvalid : function()
34343 var label = this.el.select('label', true).first();
34344 var icon = this.el.select('i.fa-star', true).first();
34350 this.fireEvent('valid', this);
34353 getName: function()
34363 * http://masonry.desandro.com
34365 * The idea is to render all the bricks based on vertical width...
34367 * The original code extends 'outlayer' - we might need to use that....
34373 * @class Roo.bootstrap.LayoutMasonry
34374 * @extends Roo.bootstrap.Component
34375 * Bootstrap Layout Masonry class
34378 * Create a new Element
34379 * @param {Object} config The config object
34382 Roo.bootstrap.LayoutMasonry = function(config){
34384 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34388 Roo.bootstrap.LayoutMasonry.register(this);
34394 * Fire after layout the items
34395 * @param {Roo.bootstrap.LayoutMasonry} this
34396 * @param {Roo.EventObject} e
34403 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34406 * @cfg {Boolean} isLayoutInstant = no animation?
34408 isLayoutInstant : false, // needed?
34411 * @cfg {Number} boxWidth width of the columns
34416 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34421 * @cfg {Number} padWidth padding below box..
34426 * @cfg {Number} gutter gutter width..
34431 * @cfg {Number} maxCols maximum number of columns
34437 * @cfg {Boolean} isAutoInitial defalut true
34439 isAutoInitial : true,
34444 * @cfg {Boolean} isHorizontal defalut false
34446 isHorizontal : false,
34448 currentSize : null,
34454 bricks: null, //CompositeElement
34458 _isLayoutInited : false,
34460 // isAlternative : false, // only use for vertical layout...
34463 * @cfg {Number} alternativePadWidth padding below box..
34465 alternativePadWidth : 50,
34467 selectedBrick : [],
34469 getAutoCreate : function(){
34471 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34475 cls: 'blog-masonary-wrapper ' + this.cls,
34477 cls : 'mas-boxes masonary'
34484 getChildContainer: function( )
34486 if (this.boxesEl) {
34487 return this.boxesEl;
34490 this.boxesEl = this.el.select('.mas-boxes').first();
34492 return this.boxesEl;
34496 initEvents : function()
34500 if(this.isAutoInitial){
34501 Roo.log('hook children rendered');
34502 this.on('childrenrendered', function() {
34503 Roo.log('children rendered');
34509 initial : function()
34511 this.selectedBrick = [];
34513 this.currentSize = this.el.getBox(true);
34515 Roo.EventManager.onWindowResize(this.resize, this);
34517 if(!this.isAutoInitial){
34525 //this.layout.defer(500,this);
34529 resize : function()
34531 var cs = this.el.getBox(true);
34534 this.currentSize.width == cs.width &&
34535 this.currentSize.x == cs.x &&
34536 this.currentSize.height == cs.height &&
34537 this.currentSize.y == cs.y
34539 Roo.log("no change in with or X or Y");
34543 this.currentSize = cs;
34549 layout : function()
34551 this._resetLayout();
34553 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34555 this.layoutItems( isInstant );
34557 this._isLayoutInited = true;
34559 this.fireEvent('layout', this);
34563 _resetLayout : function()
34565 if(this.isHorizontal){
34566 this.horizontalMeasureColumns();
34570 this.verticalMeasureColumns();
34574 verticalMeasureColumns : function()
34576 this.getContainerWidth();
34578 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34579 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34583 var boxWidth = this.boxWidth + this.padWidth;
34585 if(this.containerWidth < this.boxWidth){
34586 boxWidth = this.containerWidth
34589 var containerWidth = this.containerWidth;
34591 var cols = Math.floor(containerWidth / boxWidth);
34593 this.cols = Math.max( cols, 1 );
34595 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34597 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34599 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34601 this.colWidth = boxWidth + avail - this.padWidth;
34603 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34604 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34607 horizontalMeasureColumns : function()
34609 this.getContainerWidth();
34611 var boxWidth = this.boxWidth;
34613 if(this.containerWidth < boxWidth){
34614 boxWidth = this.containerWidth;
34617 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34619 this.el.setHeight(boxWidth);
34623 getContainerWidth : function()
34625 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34628 layoutItems : function( isInstant )
34630 Roo.log(this.bricks);
34632 var items = Roo.apply([], this.bricks);
34634 if(this.isHorizontal){
34635 this._horizontalLayoutItems( items , isInstant );
34639 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34640 // this._verticalAlternativeLayoutItems( items , isInstant );
34644 this._verticalLayoutItems( items , isInstant );
34648 _verticalLayoutItems : function ( items , isInstant)
34650 if ( !items || !items.length ) {
34655 ['xs', 'xs', 'xs', 'tall'],
34656 ['xs', 'xs', 'tall'],
34657 ['xs', 'xs', 'sm'],
34658 ['xs', 'xs', 'xs'],
34664 ['sm', 'xs', 'xs'],
34668 ['tall', 'xs', 'xs', 'xs'],
34669 ['tall', 'xs', 'xs'],
34681 Roo.each(items, function(item, k){
34683 switch (item.size) {
34684 // these layouts take up a full box,
34695 boxes.push([item]);
34718 var filterPattern = function(box, length)
34726 var pattern = box.slice(0, length);
34730 Roo.each(pattern, function(i){
34731 format.push(i.size);
34734 Roo.each(standard, function(s){
34736 if(String(s) != String(format)){
34745 if(!match && length == 1){
34750 filterPattern(box, length - 1);
34754 queue.push(pattern);
34756 box = box.slice(length, box.length);
34758 filterPattern(box, 4);
34764 Roo.each(boxes, function(box, k){
34770 if(box.length == 1){
34775 filterPattern(box, 4);
34779 this._processVerticalLayoutQueue( queue, isInstant );
34783 // _verticalAlternativeLayoutItems : function( items , isInstant )
34785 // if ( !items || !items.length ) {
34789 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34793 _horizontalLayoutItems : function ( items , isInstant)
34795 if ( !items || !items.length || items.length < 3) {
34801 var eItems = items.slice(0, 3);
34803 items = items.slice(3, items.length);
34806 ['xs', 'xs', 'xs', 'wide'],
34807 ['xs', 'xs', 'wide'],
34808 ['xs', 'xs', 'sm'],
34809 ['xs', 'xs', 'xs'],
34815 ['sm', 'xs', 'xs'],
34819 ['wide', 'xs', 'xs', 'xs'],
34820 ['wide', 'xs', 'xs'],
34833 Roo.each(items, function(item, k){
34835 switch (item.size) {
34846 boxes.push([item]);
34870 var filterPattern = function(box, length)
34878 var pattern = box.slice(0, length);
34882 Roo.each(pattern, function(i){
34883 format.push(i.size);
34886 Roo.each(standard, function(s){
34888 if(String(s) != String(format)){
34897 if(!match && length == 1){
34902 filterPattern(box, length - 1);
34906 queue.push(pattern);
34908 box = box.slice(length, box.length);
34910 filterPattern(box, 4);
34916 Roo.each(boxes, function(box, k){
34922 if(box.length == 1){
34927 filterPattern(box, 4);
34934 var pos = this.el.getBox(true);
34938 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34940 var hit_end = false;
34942 Roo.each(queue, function(box){
34946 Roo.each(box, function(b){
34948 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34958 Roo.each(box, function(b){
34960 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34963 mx = Math.max(mx, b.x);
34967 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34971 Roo.each(box, function(b){
34973 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34987 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34990 /** Sets position of item in DOM
34991 * @param {Element} item
34992 * @param {Number} x - horizontal position
34993 * @param {Number} y - vertical position
34994 * @param {Boolean} isInstant - disables transitions
34996 _processVerticalLayoutQueue : function( queue, isInstant )
34998 var pos = this.el.getBox(true);
35003 for (var i = 0; i < this.cols; i++){
35007 Roo.each(queue, function(box, k){
35009 var col = k % this.cols;
35011 Roo.each(box, function(b,kk){
35013 b.el.position('absolute');
35015 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35016 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35018 if(b.size == 'md-left' || b.size == 'md-right'){
35019 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35020 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35023 b.el.setWidth(width);
35024 b.el.setHeight(height);
35026 b.el.select('iframe',true).setSize(width,height);
35030 for (var i = 0; i < this.cols; i++){
35032 if(maxY[i] < maxY[col]){
35037 col = Math.min(col, i);
35041 x = pos.x + col * (this.colWidth + this.padWidth);
35045 var positions = [];
35047 switch (box.length){
35049 positions = this.getVerticalOneBoxColPositions(x, y, box);
35052 positions = this.getVerticalTwoBoxColPositions(x, y, box);
35055 positions = this.getVerticalThreeBoxColPositions(x, y, box);
35058 positions = this.getVerticalFourBoxColPositions(x, y, box);
35064 Roo.each(box, function(b,kk){
35066 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35068 var sz = b.el.getSize();
35070 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35078 for (var i = 0; i < this.cols; i++){
35079 mY = Math.max(mY, maxY[i]);
35082 this.el.setHeight(mY - pos.y);
35086 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35088 // var pos = this.el.getBox(true);
35091 // var maxX = pos.right;
35093 // var maxHeight = 0;
35095 // Roo.each(items, function(item, k){
35099 // item.el.position('absolute');
35101 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35103 // item.el.setWidth(width);
35105 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35107 // item.el.setHeight(height);
35110 // item.el.setXY([x, y], isInstant ? false : true);
35112 // item.el.setXY([maxX - width, y], isInstant ? false : true);
35115 // y = y + height + this.alternativePadWidth;
35117 // maxHeight = maxHeight + height + this.alternativePadWidth;
35121 // this.el.setHeight(maxHeight);
35125 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35127 var pos = this.el.getBox(true);
35132 var maxX = pos.right;
35134 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35136 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35138 Roo.each(queue, function(box, k){
35140 Roo.each(box, function(b, kk){
35142 b.el.position('absolute');
35144 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35145 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35147 if(b.size == 'md-left' || b.size == 'md-right'){
35148 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35149 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35152 b.el.setWidth(width);
35153 b.el.setHeight(height);
35161 var positions = [];
35163 switch (box.length){
35165 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35168 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35171 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35174 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35180 Roo.each(box, function(b,kk){
35182 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35184 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35192 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35194 Roo.each(eItems, function(b,k){
35196 b.size = (k == 0) ? 'sm' : 'xs';
35197 b.x = (k == 0) ? 2 : 1;
35198 b.y = (k == 0) ? 2 : 1;
35200 b.el.position('absolute');
35202 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35204 b.el.setWidth(width);
35206 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35208 b.el.setHeight(height);
35212 var positions = [];
35215 x : maxX - this.unitWidth * 2 - this.gutter,
35220 x : maxX - this.unitWidth,
35221 y : minY + (this.unitWidth + this.gutter) * 2
35225 x : maxX - this.unitWidth * 3 - this.gutter * 2,
35229 Roo.each(eItems, function(b,k){
35231 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35237 getVerticalOneBoxColPositions : function(x, y, box)
35241 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35243 if(box[0].size == 'md-left'){
35247 if(box[0].size == 'md-right'){
35252 x : x + (this.unitWidth + this.gutter) * rand,
35259 getVerticalTwoBoxColPositions : function(x, y, box)
35263 if(box[0].size == 'xs'){
35267 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35271 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35285 x : x + (this.unitWidth + this.gutter) * 2,
35286 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35293 getVerticalThreeBoxColPositions : function(x, y, box)
35297 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35305 x : x + (this.unitWidth + this.gutter) * 1,
35310 x : x + (this.unitWidth + this.gutter) * 2,
35318 if(box[0].size == 'xs' && box[1].size == 'xs'){
35327 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35331 x : x + (this.unitWidth + this.gutter) * 1,
35345 x : x + (this.unitWidth + this.gutter) * 2,
35350 x : x + (this.unitWidth + this.gutter) * 2,
35351 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35358 getVerticalFourBoxColPositions : function(x, y, box)
35362 if(box[0].size == 'xs'){
35371 y : y + (this.unitHeight + this.gutter) * 1
35376 y : y + (this.unitHeight + this.gutter) * 2
35380 x : x + (this.unitWidth + this.gutter) * 1,
35394 x : x + (this.unitWidth + this.gutter) * 2,
35399 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35400 y : y + (this.unitHeight + this.gutter) * 1
35404 x : x + (this.unitWidth + this.gutter) * 2,
35405 y : y + (this.unitWidth + this.gutter) * 2
35412 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35416 if(box[0].size == 'md-left'){
35418 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35425 if(box[0].size == 'md-right'){
35427 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35428 y : minY + (this.unitWidth + this.gutter) * 1
35434 var rand = Math.floor(Math.random() * (4 - box[0].y));
35437 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35438 y : minY + (this.unitWidth + this.gutter) * rand
35445 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35449 if(box[0].size == 'xs'){
35452 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35457 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35458 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35466 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35471 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35472 y : minY + (this.unitWidth + this.gutter) * 2
35479 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35483 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35486 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35491 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35492 y : minY + (this.unitWidth + this.gutter) * 1
35496 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35497 y : minY + (this.unitWidth + this.gutter) * 2
35504 if(box[0].size == 'xs' && box[1].size == 'xs'){
35507 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35512 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35517 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35518 y : minY + (this.unitWidth + this.gutter) * 1
35526 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35531 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35532 y : minY + (this.unitWidth + this.gutter) * 2
35536 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35537 y : minY + (this.unitWidth + this.gutter) * 2
35544 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35548 if(box[0].size == 'xs'){
35551 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35556 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35561 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),
35566 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35567 y : minY + (this.unitWidth + this.gutter) * 1
35575 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35580 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35581 y : minY + (this.unitWidth + this.gutter) * 2
35585 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35586 y : minY + (this.unitWidth + this.gutter) * 2
35590 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),
35591 y : minY + (this.unitWidth + this.gutter) * 2
35599 * remove a Masonry Brick
35600 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35602 removeBrick : function(brick_id)
35608 for (var i = 0; i<this.bricks.length; i++) {
35609 if (this.bricks[i].id == brick_id) {
35610 this.bricks.splice(i,1);
35611 this.el.dom.removeChild(Roo.get(brick_id).dom);
35618 * adds a Masonry Brick
35619 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35621 addBrick : function(cfg)
35623 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35624 //this.register(cn);
35625 cn.parentId = this.id;
35626 cn.render(this.el);
35631 * register a Masonry Brick
35632 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35635 register : function(brick)
35637 this.bricks.push(brick);
35638 brick.masonryId = this.id;
35642 * clear all the Masonry Brick
35644 clearAll : function()
35647 //this.getChildContainer().dom.innerHTML = "";
35648 this.el.dom.innerHTML = '';
35651 getSelected : function()
35653 if (!this.selectedBrick) {
35657 return this.selectedBrick;
35661 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35665 * register a Masonry Layout
35666 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35669 register : function(layout)
35671 this.groups[layout.id] = layout;
35674 * fetch a Masonry Layout based on the masonry layout ID
35675 * @param {string} the masonry layout to add
35676 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35679 get: function(layout_id) {
35680 if (typeof(this.groups[layout_id]) == 'undefined') {
35683 return this.groups[layout_id] ;
35695 * http://masonry.desandro.com
35697 * The idea is to render all the bricks based on vertical width...
35699 * The original code extends 'outlayer' - we might need to use that....
35705 * @class Roo.bootstrap.LayoutMasonryAuto
35706 * @extends Roo.bootstrap.Component
35707 * Bootstrap Layout Masonry class
35710 * Create a new Element
35711 * @param {Object} config The config object
35714 Roo.bootstrap.LayoutMasonryAuto = function(config){
35715 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35718 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35721 * @cfg {Boolean} isFitWidth - resize the width..
35723 isFitWidth : false, // options..
35725 * @cfg {Boolean} isOriginLeft = left align?
35727 isOriginLeft : true,
35729 * @cfg {Boolean} isOriginTop = top align?
35731 isOriginTop : false,
35733 * @cfg {Boolean} isLayoutInstant = no animation?
35735 isLayoutInstant : false, // needed?
35737 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35739 isResizingContainer : true,
35741 * @cfg {Number} columnWidth width of the columns
35747 * @cfg {Number} maxCols maximum number of columns
35752 * @cfg {Number} padHeight padding below box..
35758 * @cfg {Boolean} isAutoInitial defalut true
35761 isAutoInitial : true,
35767 initialColumnWidth : 0,
35768 currentSize : null,
35770 colYs : null, // array.
35777 bricks: null, //CompositeElement
35778 cols : 0, // array?
35779 // element : null, // wrapped now this.el
35780 _isLayoutInited : null,
35783 getAutoCreate : function(){
35787 cls: 'blog-masonary-wrapper ' + this.cls,
35789 cls : 'mas-boxes masonary'
35796 getChildContainer: function( )
35798 if (this.boxesEl) {
35799 return this.boxesEl;
35802 this.boxesEl = this.el.select('.mas-boxes').first();
35804 return this.boxesEl;
35808 initEvents : function()
35812 if(this.isAutoInitial){
35813 Roo.log('hook children rendered');
35814 this.on('childrenrendered', function() {
35815 Roo.log('children rendered');
35822 initial : function()
35824 this.reloadItems();
35826 this.currentSize = this.el.getBox(true);
35828 /// was window resize... - let's see if this works..
35829 Roo.EventManager.onWindowResize(this.resize, this);
35831 if(!this.isAutoInitial){
35836 this.layout.defer(500,this);
35839 reloadItems: function()
35841 this.bricks = this.el.select('.masonry-brick', true);
35843 this.bricks.each(function(b) {
35844 //Roo.log(b.getSize());
35845 if (!b.attr('originalwidth')) {
35846 b.attr('originalwidth', b.getSize().width);
35851 Roo.log(this.bricks.elements.length);
35854 resize : function()
35857 var cs = this.el.getBox(true);
35859 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35860 Roo.log("no change in with or X");
35863 this.currentSize = cs;
35867 layout : function()
35870 this._resetLayout();
35871 //this._manageStamps();
35873 // don't animate first layout
35874 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35875 this.layoutItems( isInstant );
35877 // flag for initalized
35878 this._isLayoutInited = true;
35881 layoutItems : function( isInstant )
35883 //var items = this._getItemsForLayout( this.items );
35884 // original code supports filtering layout items.. we just ignore it..
35886 this._layoutItems( this.bricks , isInstant );
35888 this._postLayout();
35890 _layoutItems : function ( items , isInstant)
35892 //this.fireEvent( 'layout', this, items );
35895 if ( !items || !items.elements.length ) {
35896 // no items, emit event with empty array
35901 items.each(function(item) {
35902 Roo.log("layout item");
35904 // get x/y object from method
35905 var position = this._getItemLayoutPosition( item );
35907 position.item = item;
35908 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35909 queue.push( position );
35912 this._processLayoutQueue( queue );
35914 /** Sets position of item in DOM
35915 * @param {Element} item
35916 * @param {Number} x - horizontal position
35917 * @param {Number} y - vertical position
35918 * @param {Boolean} isInstant - disables transitions
35920 _processLayoutQueue : function( queue )
35922 for ( var i=0, len = queue.length; i < len; i++ ) {
35923 var obj = queue[i];
35924 obj.item.position('absolute');
35925 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35931 * Any logic you want to do after each layout,
35932 * i.e. size the container
35934 _postLayout : function()
35936 this.resizeContainer();
35939 resizeContainer : function()
35941 if ( !this.isResizingContainer ) {
35944 var size = this._getContainerSize();
35946 this.el.setSize(size.width,size.height);
35947 this.boxesEl.setSize(size.width,size.height);
35953 _resetLayout : function()
35955 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35956 this.colWidth = this.el.getWidth();
35957 //this.gutter = this.el.getWidth();
35959 this.measureColumns();
35965 this.colYs.push( 0 );
35971 measureColumns : function()
35973 this.getContainerWidth();
35974 // if columnWidth is 0, default to outerWidth of first item
35975 if ( !this.columnWidth ) {
35976 var firstItem = this.bricks.first();
35977 Roo.log(firstItem);
35978 this.columnWidth = this.containerWidth;
35979 if (firstItem && firstItem.attr('originalwidth') ) {
35980 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35982 // columnWidth fall back to item of first element
35983 Roo.log("set column width?");
35984 this.initialColumnWidth = this.columnWidth ;
35986 // if first elem has no width, default to size of container
35991 if (this.initialColumnWidth) {
35992 this.columnWidth = this.initialColumnWidth;
35997 // column width is fixed at the top - however if container width get's smaller we should
36000 // this bit calcs how man columns..
36002 var columnWidth = this.columnWidth += this.gutter;
36004 // calculate columns
36005 var containerWidth = this.containerWidth + this.gutter;
36007 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
36008 // fix rounding errors, typically with gutters
36009 var excess = columnWidth - containerWidth % columnWidth;
36012 // if overshoot is less than a pixel, round up, otherwise floor it
36013 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
36014 cols = Math[ mathMethod ]( cols );
36015 this.cols = Math.max( cols, 1 );
36016 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36018 // padding positioning..
36019 var totalColWidth = this.cols * this.columnWidth;
36020 var padavail = this.containerWidth - totalColWidth;
36021 // so for 2 columns - we need 3 'pads'
36023 var padNeeded = (1+this.cols) * this.padWidth;
36025 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36027 this.columnWidth += padExtra
36028 //this.padWidth = Math.floor(padavail / ( this.cols));
36030 // adjust colum width so that padding is fixed??
36032 // we have 3 columns ... total = width * 3
36033 // we have X left over... that should be used by
36035 //if (this.expandC) {
36043 getContainerWidth : function()
36045 /* // container is parent if fit width
36046 var container = this.isFitWidth ? this.element.parentNode : this.element;
36047 // check that this.size and size are there
36048 // IE8 triggers resize on body size change, so they might not be
36050 var size = getSize( container ); //FIXME
36051 this.containerWidth = size && size.innerWidth; //FIXME
36054 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
36058 _getItemLayoutPosition : function( item ) // what is item?
36060 // we resize the item to our columnWidth..
36062 item.setWidth(this.columnWidth);
36063 item.autoBoxAdjust = false;
36065 var sz = item.getSize();
36067 // how many columns does this brick span
36068 var remainder = this.containerWidth % this.columnWidth;
36070 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36071 // round if off by 1 pixel, otherwise use ceil
36072 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
36073 colSpan = Math.min( colSpan, this.cols );
36075 // normally this should be '1' as we dont' currently allow multi width columns..
36077 var colGroup = this._getColGroup( colSpan );
36078 // get the minimum Y value from the columns
36079 var minimumY = Math.min.apply( Math, colGroup );
36080 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36082 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
36084 // position the brick
36086 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36087 y: this.currentSize.y + minimumY + this.padHeight
36091 // apply setHeight to necessary columns
36092 var setHeight = minimumY + sz.height + this.padHeight;
36093 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36095 var setSpan = this.cols + 1 - colGroup.length;
36096 for ( var i = 0; i < setSpan; i++ ) {
36097 this.colYs[ shortColIndex + i ] = setHeight ;
36104 * @param {Number} colSpan - number of columns the element spans
36105 * @returns {Array} colGroup
36107 _getColGroup : function( colSpan )
36109 if ( colSpan < 2 ) {
36110 // if brick spans only one column, use all the column Ys
36115 // how many different places could this brick fit horizontally
36116 var groupCount = this.cols + 1 - colSpan;
36117 // for each group potential horizontal position
36118 for ( var i = 0; i < groupCount; i++ ) {
36119 // make an array of colY values for that one group
36120 var groupColYs = this.colYs.slice( i, i + colSpan );
36121 // and get the max value of the array
36122 colGroup[i] = Math.max.apply( Math, groupColYs );
36127 _manageStamp : function( stamp )
36129 var stampSize = stamp.getSize();
36130 var offset = stamp.getBox();
36131 // get the columns that this stamp affects
36132 var firstX = this.isOriginLeft ? offset.x : offset.right;
36133 var lastX = firstX + stampSize.width;
36134 var firstCol = Math.floor( firstX / this.columnWidth );
36135 firstCol = Math.max( 0, firstCol );
36137 var lastCol = Math.floor( lastX / this.columnWidth );
36138 // lastCol should not go over if multiple of columnWidth #425
36139 lastCol -= lastX % this.columnWidth ? 0 : 1;
36140 lastCol = Math.min( this.cols - 1, lastCol );
36142 // set colYs to bottom of the stamp
36143 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36146 for ( var i = firstCol; i <= lastCol; i++ ) {
36147 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36152 _getContainerSize : function()
36154 this.maxY = Math.max.apply( Math, this.colYs );
36159 if ( this.isFitWidth ) {
36160 size.width = this._getContainerFitWidth();
36166 _getContainerFitWidth : function()
36168 var unusedCols = 0;
36169 // count unused columns
36172 if ( this.colYs[i] !== 0 ) {
36177 // fit container to columns that have been used
36178 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36181 needsResizeLayout : function()
36183 var previousWidth = this.containerWidth;
36184 this.getContainerWidth();
36185 return previousWidth !== this.containerWidth;
36200 * @class Roo.bootstrap.MasonryBrick
36201 * @extends Roo.bootstrap.Component
36202 * Bootstrap MasonryBrick class
36205 * Create a new MasonryBrick
36206 * @param {Object} config The config object
36209 Roo.bootstrap.MasonryBrick = function(config){
36211 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36213 Roo.bootstrap.MasonryBrick.register(this);
36219 * When a MasonryBrick is clcik
36220 * @param {Roo.bootstrap.MasonryBrick} this
36221 * @param {Roo.EventObject} e
36227 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
36230 * @cfg {String} title
36234 * @cfg {String} html
36238 * @cfg {String} bgimage
36242 * @cfg {String} videourl
36246 * @cfg {String} cls
36250 * @cfg {String} href
36254 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36259 * @cfg {String} placetitle (center|bottom)
36264 * @cfg {Boolean} isFitContainer defalut true
36266 isFitContainer : true,
36269 * @cfg {Boolean} preventDefault defalut false
36271 preventDefault : false,
36274 * @cfg {Boolean} inverse defalut false
36276 maskInverse : false,
36278 getAutoCreate : function()
36280 if(!this.isFitContainer){
36281 return this.getSplitAutoCreate();
36284 var cls = 'masonry-brick masonry-brick-full';
36286 if(this.href.length){
36287 cls += ' masonry-brick-link';
36290 if(this.bgimage.length){
36291 cls += ' masonry-brick-image';
36294 if(this.maskInverse){
36295 cls += ' mask-inverse';
36298 if(!this.html.length && !this.maskInverse && !this.videourl.length){
36299 cls += ' enable-mask';
36303 cls += ' masonry-' + this.size + '-brick';
36306 if(this.placetitle.length){
36308 switch (this.placetitle) {
36310 cls += ' masonry-center-title';
36313 cls += ' masonry-bottom-title';
36320 if(!this.html.length && !this.bgimage.length){
36321 cls += ' masonry-center-title';
36324 if(!this.html.length && this.bgimage.length){
36325 cls += ' masonry-bottom-title';
36330 cls += ' ' + this.cls;
36334 tag: (this.href.length) ? 'a' : 'div',
36339 cls: 'masonry-brick-mask'
36343 cls: 'masonry-brick-paragraph',
36349 if(this.href.length){
36350 cfg.href = this.href;
36353 var cn = cfg.cn[1].cn;
36355 if(this.title.length){
36358 cls: 'masonry-brick-title',
36363 if(this.html.length){
36366 cls: 'masonry-brick-text',
36371 if (!this.title.length && !this.html.length) {
36372 cfg.cn[1].cls += ' hide';
36375 if(this.bgimage.length){
36378 cls: 'masonry-brick-image-view',
36383 if(this.videourl.length){
36384 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36385 // youtube support only?
36388 cls: 'masonry-brick-image-view',
36391 allowfullscreen : true
36399 getSplitAutoCreate : function()
36401 var cls = 'masonry-brick masonry-brick-split';
36403 if(this.href.length){
36404 cls += ' masonry-brick-link';
36407 if(this.bgimage.length){
36408 cls += ' masonry-brick-image';
36412 cls += ' masonry-' + this.size + '-brick';
36415 switch (this.placetitle) {
36417 cls += ' masonry-center-title';
36420 cls += ' masonry-bottom-title';
36423 if(!this.bgimage.length){
36424 cls += ' masonry-center-title';
36427 if(this.bgimage.length){
36428 cls += ' masonry-bottom-title';
36434 cls += ' ' + this.cls;
36438 tag: (this.href.length) ? 'a' : 'div',
36443 cls: 'masonry-brick-split-head',
36447 cls: 'masonry-brick-paragraph',
36454 cls: 'masonry-brick-split-body',
36460 if(this.href.length){
36461 cfg.href = this.href;
36464 if(this.title.length){
36465 cfg.cn[0].cn[0].cn.push({
36467 cls: 'masonry-brick-title',
36472 if(this.html.length){
36473 cfg.cn[1].cn.push({
36475 cls: 'masonry-brick-text',
36480 if(this.bgimage.length){
36481 cfg.cn[0].cn.push({
36483 cls: 'masonry-brick-image-view',
36488 if(this.videourl.length){
36489 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36490 // youtube support only?
36491 cfg.cn[0].cn.cn.push({
36493 cls: 'masonry-brick-image-view',
36496 allowfullscreen : true
36503 initEvents: function()
36505 switch (this.size) {
36538 this.el.on('touchstart', this.onTouchStart, this);
36539 this.el.on('touchmove', this.onTouchMove, this);
36540 this.el.on('touchend', this.onTouchEnd, this);
36541 this.el.on('contextmenu', this.onContextMenu, this);
36543 this.el.on('mouseenter' ,this.enter, this);
36544 this.el.on('mouseleave', this.leave, this);
36545 this.el.on('click', this.onClick, this);
36548 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36549 this.parent().bricks.push(this);
36554 onClick: function(e, el)
36556 var time = this.endTimer - this.startTimer;
36557 // Roo.log(e.preventDefault());
36560 e.preventDefault();
36565 if(!this.preventDefault){
36569 e.preventDefault();
36571 if (this.activeClass != '') {
36572 this.selectBrick();
36575 this.fireEvent('click', this, e);
36578 enter: function(e, el)
36580 e.preventDefault();
36582 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36586 if(this.bgimage.length && this.html.length){
36587 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36591 leave: function(e, el)
36593 e.preventDefault();
36595 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36599 if(this.bgimage.length && this.html.length){
36600 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36604 onTouchStart: function(e, el)
36606 // e.preventDefault();
36608 this.touchmoved = false;
36610 if(!this.isFitContainer){
36614 if(!this.bgimage.length || !this.html.length){
36618 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36620 this.timer = new Date().getTime();
36624 onTouchMove: function(e, el)
36626 this.touchmoved = true;
36629 onContextMenu : function(e,el)
36631 e.preventDefault();
36632 e.stopPropagation();
36636 onTouchEnd: function(e, el)
36638 // e.preventDefault();
36640 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36647 if(!this.bgimage.length || !this.html.length){
36649 if(this.href.length){
36650 window.location.href = this.href;
36656 if(!this.isFitContainer){
36660 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36662 window.location.href = this.href;
36665 //selection on single brick only
36666 selectBrick : function() {
36668 if (!this.parentId) {
36672 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36673 var index = m.selectedBrick.indexOf(this.id);
36676 m.selectedBrick.splice(index,1);
36677 this.el.removeClass(this.activeClass);
36681 for(var i = 0; i < m.selectedBrick.length; i++) {
36682 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36683 b.el.removeClass(b.activeClass);
36686 m.selectedBrick = [];
36688 m.selectedBrick.push(this.id);
36689 this.el.addClass(this.activeClass);
36693 isSelected : function(){
36694 return this.el.hasClass(this.activeClass);
36699 Roo.apply(Roo.bootstrap.MasonryBrick, {
36702 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36704 * register a Masonry Brick
36705 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36708 register : function(brick)
36710 //this.groups[brick.id] = brick;
36711 this.groups.add(brick.id, brick);
36714 * fetch a masonry brick based on the masonry brick ID
36715 * @param {string} the masonry brick to add
36716 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36719 get: function(brick_id)
36721 // if (typeof(this.groups[brick_id]) == 'undefined') {
36724 // return this.groups[brick_id] ;
36726 if(this.groups.key(brick_id)) {
36727 return this.groups.key(brick_id);
36745 * @class Roo.bootstrap.Brick
36746 * @extends Roo.bootstrap.Component
36747 * Bootstrap Brick class
36750 * Create a new Brick
36751 * @param {Object} config The config object
36754 Roo.bootstrap.Brick = function(config){
36755 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36761 * When a Brick is click
36762 * @param {Roo.bootstrap.Brick} this
36763 * @param {Roo.EventObject} e
36769 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36772 * @cfg {String} title
36776 * @cfg {String} html
36780 * @cfg {String} bgimage
36784 * @cfg {String} cls
36788 * @cfg {String} href
36792 * @cfg {String} video
36796 * @cfg {Boolean} square
36800 getAutoCreate : function()
36802 var cls = 'roo-brick';
36804 if(this.href.length){
36805 cls += ' roo-brick-link';
36808 if(this.bgimage.length){
36809 cls += ' roo-brick-image';
36812 if(!this.html.length && !this.bgimage.length){
36813 cls += ' roo-brick-center-title';
36816 if(!this.html.length && this.bgimage.length){
36817 cls += ' roo-brick-bottom-title';
36821 cls += ' ' + this.cls;
36825 tag: (this.href.length) ? 'a' : 'div',
36830 cls: 'roo-brick-paragraph',
36836 if(this.href.length){
36837 cfg.href = this.href;
36840 var cn = cfg.cn[0].cn;
36842 if(this.title.length){
36845 cls: 'roo-brick-title',
36850 if(this.html.length){
36853 cls: 'roo-brick-text',
36860 if(this.bgimage.length){
36863 cls: 'roo-brick-image-view',
36871 initEvents: function()
36873 if(this.title.length || this.html.length){
36874 this.el.on('mouseenter' ,this.enter, this);
36875 this.el.on('mouseleave', this.leave, this);
36878 Roo.EventManager.onWindowResize(this.resize, this);
36880 if(this.bgimage.length){
36881 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36882 this.imageEl.on('load', this.onImageLoad, this);
36889 onImageLoad : function()
36894 resize : function()
36896 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36898 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36900 if(this.bgimage.length){
36901 var image = this.el.select('.roo-brick-image-view', true).first();
36903 image.setWidth(paragraph.getWidth());
36906 image.setHeight(paragraph.getWidth());
36909 this.el.setHeight(image.getHeight());
36910 paragraph.setHeight(image.getHeight());
36916 enter: function(e, el)
36918 e.preventDefault();
36920 if(this.bgimage.length){
36921 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36922 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36926 leave: function(e, el)
36928 e.preventDefault();
36930 if(this.bgimage.length){
36931 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36932 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36947 * @class Roo.bootstrap.NumberField
36948 * @extends Roo.bootstrap.Input
36949 * Bootstrap NumberField class
36955 * Create a new NumberField
36956 * @param {Object} config The config object
36959 Roo.bootstrap.NumberField = function(config){
36960 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36963 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36966 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36968 allowDecimals : true,
36970 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36972 decimalSeparator : ".",
36974 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36976 decimalPrecision : 2,
36978 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36980 allowNegative : true,
36983 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36987 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36989 minValue : Number.NEGATIVE_INFINITY,
36991 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36993 maxValue : Number.MAX_VALUE,
36995 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36997 minText : "The minimum value for this field is {0}",
36999 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
37001 maxText : "The maximum value for this field is {0}",
37003 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
37004 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
37006 nanText : "{0} is not a valid number",
37008 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
37010 thousandsDelimiter : false,
37012 * @cfg {String} valueAlign alignment of value
37014 valueAlign : "left",
37016 getAutoCreate : function()
37018 var hiddenInput = {
37022 cls: 'hidden-number-input'
37026 hiddenInput.name = this.name;
37031 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37033 this.name = hiddenInput.name;
37035 if(cfg.cn.length > 0) {
37036 cfg.cn.push(hiddenInput);
37043 initEvents : function()
37045 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37047 var allowed = "0123456789";
37049 if(this.allowDecimals){
37050 allowed += this.decimalSeparator;
37053 if(this.allowNegative){
37057 if(this.thousandsDelimiter) {
37061 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37063 var keyPress = function(e){
37065 var k = e.getKey();
37067 var c = e.getCharCode();
37070 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37071 allowed.indexOf(String.fromCharCode(c)) === -1
37077 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37081 if(allowed.indexOf(String.fromCharCode(c)) === -1){
37086 this.el.on("keypress", keyPress, this);
37089 validateValue : function(value)
37092 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37096 var num = this.parseValue(value);
37099 this.markInvalid(String.format(this.nanText, value));
37103 if(num < this.minValue){
37104 this.markInvalid(String.format(this.minText, this.minValue));
37108 if(num > this.maxValue){
37109 this.markInvalid(String.format(this.maxText, this.maxValue));
37116 getValue : function()
37118 var v = this.hiddenEl().getValue();
37120 return this.fixPrecision(this.parseValue(v));
37123 parseValue : function(value)
37125 if(this.thousandsDelimiter) {
37127 r = new RegExp(",", "g");
37128 value = value.replace(r, "");
37131 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37132 return isNaN(value) ? '' : value;
37135 fixPrecision : function(value)
37137 if(this.thousandsDelimiter) {
37139 r = new RegExp(",", "g");
37140 value = value.replace(r, "");
37143 var nan = isNaN(value);
37145 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37146 return nan ? '' : value;
37148 return parseFloat(value).toFixed(this.decimalPrecision);
37151 setValue : function(v)
37153 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37159 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37161 this.inputEl().dom.value = (v == '') ? '' :
37162 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37164 if(!this.allowZero && v === '0') {
37165 this.hiddenEl().dom.value = '';
37166 this.inputEl().dom.value = '';
37173 decimalPrecisionFcn : function(v)
37175 return Math.floor(v);
37178 beforeBlur : function()
37180 var v = this.parseValue(this.getRawValue());
37182 if(v || v === 0 || v === ''){
37187 hiddenEl : function()
37189 return this.el.select('input.hidden-number-input',true).first();
37201 * @class Roo.bootstrap.DocumentSlider
37202 * @extends Roo.bootstrap.Component
37203 * Bootstrap DocumentSlider class
37206 * Create a new DocumentViewer
37207 * @param {Object} config The config object
37210 Roo.bootstrap.DocumentSlider = function(config){
37211 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37218 * Fire after initEvent
37219 * @param {Roo.bootstrap.DocumentSlider} this
37224 * Fire after update
37225 * @param {Roo.bootstrap.DocumentSlider} this
37231 * @param {Roo.bootstrap.DocumentSlider} this
37237 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
37243 getAutoCreate : function()
37247 cls : 'roo-document-slider',
37251 cls : 'roo-document-slider-header',
37255 cls : 'roo-document-slider-header-title'
37261 cls : 'roo-document-slider-body',
37265 cls : 'roo-document-slider-prev',
37269 cls : 'fa fa-chevron-left'
37275 cls : 'roo-document-slider-thumb',
37279 cls : 'roo-document-slider-image'
37285 cls : 'roo-document-slider-next',
37289 cls : 'fa fa-chevron-right'
37301 initEvents : function()
37303 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37304 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37306 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37307 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37309 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37310 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37312 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37313 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37315 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37316 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37318 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37319 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37321 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37322 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37324 this.thumbEl.on('click', this.onClick, this);
37326 this.prevIndicator.on('click', this.prev, this);
37328 this.nextIndicator.on('click', this.next, this);
37332 initial : function()
37334 if(this.files.length){
37335 this.indicator = 1;
37339 this.fireEvent('initial', this);
37342 update : function()
37344 this.imageEl.attr('src', this.files[this.indicator - 1]);
37346 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37348 this.prevIndicator.show();
37350 if(this.indicator == 1){
37351 this.prevIndicator.hide();
37354 this.nextIndicator.show();
37356 if(this.indicator == this.files.length){
37357 this.nextIndicator.hide();
37360 this.thumbEl.scrollTo('top');
37362 this.fireEvent('update', this);
37365 onClick : function(e)
37367 e.preventDefault();
37369 this.fireEvent('click', this);
37374 e.preventDefault();
37376 this.indicator = Math.max(1, this.indicator - 1);
37383 e.preventDefault();
37385 this.indicator = Math.min(this.files.length, this.indicator + 1);
37399 * @class Roo.bootstrap.RadioSet
37400 * @extends Roo.bootstrap.Input
37401 * Bootstrap RadioSet class
37402 * @cfg {String} indicatorpos (left|right) default left
37403 * @cfg {Boolean} inline (true|false) inline the element (default true)
37404 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37406 * Create a new RadioSet
37407 * @param {Object} config The config object
37410 Roo.bootstrap.RadioSet = function(config){
37412 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37416 Roo.bootstrap.RadioSet.register(this);
37421 * Fires when the element is checked or unchecked.
37422 * @param {Roo.bootstrap.RadioSet} this This radio
37423 * @param {Roo.bootstrap.Radio} item The checked item
37428 * Fires when the element is click.
37429 * @param {Roo.bootstrap.RadioSet} this This radio set
37430 * @param {Roo.bootstrap.Radio} item The checked item
37431 * @param {Roo.EventObject} e The event object
37438 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37446 indicatorpos : 'left',
37448 getAutoCreate : function()
37452 cls : 'roo-radio-set-label',
37456 html : this.fieldLabel
37460 if (Roo.bootstrap.version == 3) {
37463 if(this.indicatorpos == 'left'){
37466 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37467 tooltip : 'This field is required'
37472 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37473 tooltip : 'This field is required'
37479 cls : 'roo-radio-set-items'
37482 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37484 if (align === 'left' && this.fieldLabel.length) {
37487 cls : "roo-radio-set-right",
37493 if(this.labelWidth > 12){
37494 label.style = "width: " + this.labelWidth + 'px';
37497 if(this.labelWidth < 13 && this.labelmd == 0){
37498 this.labelmd = this.labelWidth;
37501 if(this.labellg > 0){
37502 label.cls += ' col-lg-' + this.labellg;
37503 items.cls += ' col-lg-' + (12 - this.labellg);
37506 if(this.labelmd > 0){
37507 label.cls += ' col-md-' + this.labelmd;
37508 items.cls += ' col-md-' + (12 - this.labelmd);
37511 if(this.labelsm > 0){
37512 label.cls += ' col-sm-' + this.labelsm;
37513 items.cls += ' col-sm-' + (12 - this.labelsm);
37516 if(this.labelxs > 0){
37517 label.cls += ' col-xs-' + this.labelxs;
37518 items.cls += ' col-xs-' + (12 - this.labelxs);
37524 cls : 'roo-radio-set',
37528 cls : 'roo-radio-set-input',
37531 value : this.value ? this.value : ''
37538 if(this.weight.length){
37539 cfg.cls += ' roo-radio-' + this.weight;
37543 cfg.cls += ' roo-radio-set-inline';
37547 ['xs','sm','md','lg'].map(function(size){
37548 if (settings[size]) {
37549 cfg.cls += ' col-' + size + '-' + settings[size];
37557 initEvents : function()
37559 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37560 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37562 if(!this.fieldLabel.length){
37563 this.labelEl.hide();
37566 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37567 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37569 this.indicator = this.indicatorEl();
37571 if(this.indicator){
37572 this.indicator.addClass('invisible');
37575 this.originalValue = this.getValue();
37579 inputEl: function ()
37581 return this.el.select('.roo-radio-set-input', true).first();
37584 getChildContainer : function()
37586 return this.itemsEl;
37589 register : function(item)
37591 this.radioes.push(item);
37595 validate : function()
37597 if(this.getVisibilityEl().hasClass('hidden')){
37603 Roo.each(this.radioes, function(i){
37612 if(this.allowBlank) {
37616 if(this.disabled || valid){
37621 this.markInvalid();
37626 markValid : function()
37628 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37629 this.indicatorEl().removeClass('visible');
37630 this.indicatorEl().addClass('invisible');
37634 if (Roo.bootstrap.version == 3) {
37635 this.el.removeClass([this.invalidClass, this.validClass]);
37636 this.el.addClass(this.validClass);
37638 this.el.removeClass(['is-invalid','is-valid']);
37639 this.el.addClass(['is-valid']);
37641 this.fireEvent('valid', this);
37644 markInvalid : function(msg)
37646 if(this.allowBlank || this.disabled){
37650 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37651 this.indicatorEl().removeClass('invisible');
37652 this.indicatorEl().addClass('visible');
37654 if (Roo.bootstrap.version == 3) {
37655 this.el.removeClass([this.invalidClass, this.validClass]);
37656 this.el.addClass(this.invalidClass);
37658 this.el.removeClass(['is-invalid','is-valid']);
37659 this.el.addClass(['is-invalid']);
37662 this.fireEvent('invalid', this, msg);
37666 setValue : function(v, suppressEvent)
37668 if(this.value === v){
37675 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37678 Roo.each(this.radioes, function(i){
37680 i.el.removeClass('checked');
37683 Roo.each(this.radioes, function(i){
37685 if(i.value === v || i.value.toString() === v.toString()){
37687 i.el.addClass('checked');
37689 if(suppressEvent !== true){
37690 this.fireEvent('check', this, i);
37701 clearInvalid : function(){
37703 if(!this.el || this.preventMark){
37707 this.el.removeClass([this.invalidClass]);
37709 this.fireEvent('valid', this);
37714 Roo.apply(Roo.bootstrap.RadioSet, {
37718 register : function(set)
37720 this.groups[set.name] = set;
37723 get: function(name)
37725 if (typeof(this.groups[name]) == 'undefined') {
37729 return this.groups[name] ;
37735 * Ext JS Library 1.1.1
37736 * Copyright(c) 2006-2007, Ext JS, LLC.
37738 * Originally Released Under LGPL - original licence link has changed is not relivant.
37741 * <script type="text/javascript">
37746 * @class Roo.bootstrap.SplitBar
37747 * @extends Roo.util.Observable
37748 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37752 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37753 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37754 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37755 split.minSize = 100;
37756 split.maxSize = 600;
37757 split.animate = true;
37758 split.on('moved', splitterMoved);
37761 * Create a new SplitBar
37762 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37763 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37764 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37765 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37766 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37767 position of the SplitBar).
37769 Roo.bootstrap.SplitBar = function(cfg){
37774 // dragElement : elm
37775 // resizingElement: el,
37777 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37778 // placement : Roo.bootstrap.SplitBar.LEFT ,
37779 // existingProxy ???
37782 this.el = Roo.get(cfg.dragElement, true);
37783 this.el.dom.unselectable = "on";
37785 this.resizingEl = Roo.get(cfg.resizingElement, true);
37789 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37790 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37793 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37796 * The minimum size of the resizing element. (Defaults to 0)
37802 * The maximum size of the resizing element. (Defaults to 2000)
37805 this.maxSize = 2000;
37808 * Whether to animate the transition to the new size
37811 this.animate = false;
37814 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37817 this.useShim = false;
37822 if(!cfg.existingProxy){
37824 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37826 this.proxy = Roo.get(cfg.existingProxy).dom;
37829 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37832 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37835 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37838 this.dragSpecs = {};
37841 * @private The adapter to use to positon and resize elements
37843 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37844 this.adapter.init(this);
37846 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37848 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37849 this.el.addClass("roo-splitbar-h");
37852 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37853 this.el.addClass("roo-splitbar-v");
37859 * Fires when the splitter is moved (alias for {@link #event-moved})
37860 * @param {Roo.bootstrap.SplitBar} this
37861 * @param {Number} newSize the new width or height
37866 * Fires when the splitter is moved
37867 * @param {Roo.bootstrap.SplitBar} this
37868 * @param {Number} newSize the new width or height
37872 * @event beforeresize
37873 * Fires before the splitter is dragged
37874 * @param {Roo.bootstrap.SplitBar} this
37876 "beforeresize" : true,
37878 "beforeapply" : true
37881 Roo.util.Observable.call(this);
37884 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37885 onStartProxyDrag : function(x, y){
37886 this.fireEvent("beforeresize", this);
37888 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37890 o.enableDisplayMode("block");
37891 // all splitbars share the same overlay
37892 Roo.bootstrap.SplitBar.prototype.overlay = o;
37894 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37895 this.overlay.show();
37896 Roo.get(this.proxy).setDisplayed("block");
37897 var size = this.adapter.getElementSize(this);
37898 this.activeMinSize = this.getMinimumSize();;
37899 this.activeMaxSize = this.getMaximumSize();;
37900 var c1 = size - this.activeMinSize;
37901 var c2 = Math.max(this.activeMaxSize - size, 0);
37902 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37903 this.dd.resetConstraints();
37904 this.dd.setXConstraint(
37905 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37906 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37908 this.dd.setYConstraint(0, 0);
37910 this.dd.resetConstraints();
37911 this.dd.setXConstraint(0, 0);
37912 this.dd.setYConstraint(
37913 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37914 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37917 this.dragSpecs.startSize = size;
37918 this.dragSpecs.startPoint = [x, y];
37919 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37923 * @private Called after the drag operation by the DDProxy
37925 onEndProxyDrag : function(e){
37926 Roo.get(this.proxy).setDisplayed(false);
37927 var endPoint = Roo.lib.Event.getXY(e);
37929 this.overlay.hide();
37932 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37933 newSize = this.dragSpecs.startSize +
37934 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37935 endPoint[0] - this.dragSpecs.startPoint[0] :
37936 this.dragSpecs.startPoint[0] - endPoint[0]
37939 newSize = this.dragSpecs.startSize +
37940 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37941 endPoint[1] - this.dragSpecs.startPoint[1] :
37942 this.dragSpecs.startPoint[1] - endPoint[1]
37945 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37946 if(newSize != this.dragSpecs.startSize){
37947 if(this.fireEvent('beforeapply', this, newSize) !== false){
37948 this.adapter.setElementSize(this, newSize);
37949 this.fireEvent("moved", this, newSize);
37950 this.fireEvent("resize", this, newSize);
37956 * Get the adapter this SplitBar uses
37957 * @return The adapter object
37959 getAdapter : function(){
37960 return this.adapter;
37964 * Set the adapter this SplitBar uses
37965 * @param {Object} adapter A SplitBar adapter object
37967 setAdapter : function(adapter){
37968 this.adapter = adapter;
37969 this.adapter.init(this);
37973 * Gets the minimum size for the resizing element
37974 * @return {Number} The minimum size
37976 getMinimumSize : function(){
37977 return this.minSize;
37981 * Sets the minimum size for the resizing element
37982 * @param {Number} minSize The minimum size
37984 setMinimumSize : function(minSize){
37985 this.minSize = minSize;
37989 * Gets the maximum size for the resizing element
37990 * @return {Number} The maximum size
37992 getMaximumSize : function(){
37993 return this.maxSize;
37997 * Sets the maximum size for the resizing element
37998 * @param {Number} maxSize The maximum size
38000 setMaximumSize : function(maxSize){
38001 this.maxSize = maxSize;
38005 * Sets the initialize size for the resizing element
38006 * @param {Number} size The initial size
38008 setCurrentSize : function(size){
38009 var oldAnimate = this.animate;
38010 this.animate = false;
38011 this.adapter.setElementSize(this, size);
38012 this.animate = oldAnimate;
38016 * Destroy this splitbar.
38017 * @param {Boolean} removeEl True to remove the element
38019 destroy : function(removeEl){
38021 this.shim.remove();
38024 this.proxy.parentNode.removeChild(this.proxy);
38032 * @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.
38034 Roo.bootstrap.SplitBar.createProxy = function(dir){
38035 var proxy = new Roo.Element(document.createElement("div"));
38036 proxy.unselectable();
38037 var cls = 'roo-splitbar-proxy';
38038 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38039 document.body.appendChild(proxy.dom);
38044 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38045 * Default Adapter. It assumes the splitter and resizing element are not positioned
38046 * elements and only gets/sets the width of the element. Generally used for table based layouts.
38048 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38051 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38052 // do nothing for now
38053 init : function(s){
38057 * Called before drag operations to get the current size of the resizing element.
38058 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38060 getElementSize : function(s){
38061 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38062 return s.resizingEl.getWidth();
38064 return s.resizingEl.getHeight();
38069 * Called after drag operations to set the size of the resizing element.
38070 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38071 * @param {Number} newSize The new size to set
38072 * @param {Function} onComplete A function to be invoked when resizing is complete
38074 setElementSize : function(s, newSize, onComplete){
38075 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38077 s.resizingEl.setWidth(newSize);
38079 onComplete(s, newSize);
38082 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38087 s.resizingEl.setHeight(newSize);
38089 onComplete(s, newSize);
38092 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38099 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38100 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38101 * Adapter that moves the splitter element to align with the resized sizing element.
38102 * Used with an absolute positioned SplitBar.
38103 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38104 * document.body, make sure you assign an id to the body element.
38106 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38107 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38108 this.container = Roo.get(container);
38111 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38112 init : function(s){
38113 this.basic.init(s);
38116 getElementSize : function(s){
38117 return this.basic.getElementSize(s);
38120 setElementSize : function(s, newSize, onComplete){
38121 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38124 moveSplitter : function(s){
38125 var yes = Roo.bootstrap.SplitBar;
38126 switch(s.placement){
38128 s.el.setX(s.resizingEl.getRight());
38131 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38134 s.el.setY(s.resizingEl.getBottom());
38137 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38144 * Orientation constant - Create a vertical SplitBar
38148 Roo.bootstrap.SplitBar.VERTICAL = 1;
38151 * Orientation constant - Create a horizontal SplitBar
38155 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38158 * Placement constant - The resizing element is to the left of the splitter element
38162 Roo.bootstrap.SplitBar.LEFT = 1;
38165 * Placement constant - The resizing element is to the right of the splitter element
38169 Roo.bootstrap.SplitBar.RIGHT = 2;
38172 * Placement constant - The resizing element is positioned above the splitter element
38176 Roo.bootstrap.SplitBar.TOP = 3;
38179 * Placement constant - The resizing element is positioned under splitter element
38183 Roo.bootstrap.SplitBar.BOTTOM = 4;
38184 Roo.namespace("Roo.bootstrap.layout");/*
38186 * Ext JS Library 1.1.1
38187 * Copyright(c) 2006-2007, Ext JS, LLC.
38189 * Originally Released Under LGPL - original licence link has changed is not relivant.
38192 * <script type="text/javascript">
38196 * @class Roo.bootstrap.layout.Manager
38197 * @extends Roo.bootstrap.Component
38198 * Base class for layout managers.
38200 Roo.bootstrap.layout.Manager = function(config)
38202 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38208 /** false to disable window resize monitoring @type Boolean */
38209 this.monitorWindowResize = true;
38214 * Fires when a layout is performed.
38215 * @param {Roo.LayoutManager} this
38219 * @event regionresized
38220 * Fires when the user resizes a region.
38221 * @param {Roo.LayoutRegion} region The resized region
38222 * @param {Number} newSize The new size (width for east/west, height for north/south)
38224 "regionresized" : true,
38226 * @event regioncollapsed
38227 * Fires when a region is collapsed.
38228 * @param {Roo.LayoutRegion} region The collapsed region
38230 "regioncollapsed" : true,
38232 * @event regionexpanded
38233 * Fires when a region is expanded.
38234 * @param {Roo.LayoutRegion} region The expanded region
38236 "regionexpanded" : true
38238 this.updating = false;
38241 this.el = Roo.get(config.el);
38247 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38252 monitorWindowResize : true,
38258 onRender : function(ct, position)
38261 this.el = Roo.get(ct);
38264 //this.fireEvent('render',this);
38268 initEvents: function()
38272 // ie scrollbar fix
38273 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38274 document.body.scroll = "no";
38275 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38276 this.el.position('relative');
38278 this.id = this.el.id;
38279 this.el.addClass("roo-layout-container");
38280 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38281 if(this.el.dom != document.body ) {
38282 this.el.on('resize', this.layout,this);
38283 this.el.on('show', this.layout,this);
38289 * Returns true if this layout is currently being updated
38290 * @return {Boolean}
38292 isUpdating : function(){
38293 return this.updating;
38297 * Suspend the LayoutManager from doing auto-layouts while
38298 * making multiple add or remove calls
38300 beginUpdate : function(){
38301 this.updating = true;
38305 * Restore auto-layouts and optionally disable the manager from performing a layout
38306 * @param {Boolean} noLayout true to disable a layout update
38308 endUpdate : function(noLayout){
38309 this.updating = false;
38315 layout: function(){
38319 onRegionResized : function(region, newSize){
38320 this.fireEvent("regionresized", region, newSize);
38324 onRegionCollapsed : function(region){
38325 this.fireEvent("regioncollapsed", region);
38328 onRegionExpanded : function(region){
38329 this.fireEvent("regionexpanded", region);
38333 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38334 * performs box-model adjustments.
38335 * @return {Object} The size as an object {width: (the width), height: (the height)}
38337 getViewSize : function()
38340 if(this.el.dom != document.body){
38341 size = this.el.getSize();
38343 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38345 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38346 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38351 * Returns the Element this layout is bound to.
38352 * @return {Roo.Element}
38354 getEl : function(){
38359 * Returns the specified region.
38360 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38361 * @return {Roo.LayoutRegion}
38363 getRegion : function(target){
38364 return this.regions[target.toLowerCase()];
38367 onWindowResize : function(){
38368 if(this.monitorWindowResize){
38375 * Ext JS Library 1.1.1
38376 * Copyright(c) 2006-2007, Ext JS, LLC.
38378 * Originally Released Under LGPL - original licence link has changed is not relivant.
38381 * <script type="text/javascript">
38384 * @class Roo.bootstrap.layout.Border
38385 * @extends Roo.bootstrap.layout.Manager
38387 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38388 * please see: examples/bootstrap/nested.html<br><br>
38390 <b>The container the layout is rendered into can be either the body element or any other element.
38391 If it is not the body element, the container needs to either be an absolute positioned element,
38392 or you will need to add "position:relative" to the css of the container. You will also need to specify
38393 the container size if it is not the body element.</b>
38396 * Create a new Border
38397 * @param {Object} config Configuration options
38399 Roo.bootstrap.layout.Border = function(config){
38400 config = config || {};
38401 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38405 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38406 if(config[region]){
38407 config[region].region = region;
38408 this.addRegion(config[region]);
38414 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38416 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38418 parent : false, // this might point to a 'nest' or a ???
38421 * Creates and adds a new region if it doesn't already exist.
38422 * @param {String} target The target region key (north, south, east, west or center).
38423 * @param {Object} config The regions config object
38424 * @return {BorderLayoutRegion} The new region
38426 addRegion : function(config)
38428 if(!this.regions[config.region]){
38429 var r = this.factory(config);
38430 this.bindRegion(r);
38432 return this.regions[config.region];
38436 bindRegion : function(r){
38437 this.regions[r.config.region] = r;
38439 r.on("visibilitychange", this.layout, this);
38440 r.on("paneladded", this.layout, this);
38441 r.on("panelremoved", this.layout, this);
38442 r.on("invalidated", this.layout, this);
38443 r.on("resized", this.onRegionResized, this);
38444 r.on("collapsed", this.onRegionCollapsed, this);
38445 r.on("expanded", this.onRegionExpanded, this);
38449 * Performs a layout update.
38451 layout : function()
38453 if(this.updating) {
38457 // render all the rebions if they have not been done alreayd?
38458 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38459 if(this.regions[region] && !this.regions[region].bodyEl){
38460 this.regions[region].onRender(this.el)
38464 var size = this.getViewSize();
38465 var w = size.width;
38466 var h = size.height;
38471 //var x = 0, y = 0;
38473 var rs = this.regions;
38474 var north = rs["north"];
38475 var south = rs["south"];
38476 var west = rs["west"];
38477 var east = rs["east"];
38478 var center = rs["center"];
38479 //if(this.hideOnLayout){ // not supported anymore
38480 //c.el.setStyle("display", "none");
38482 if(north && north.isVisible()){
38483 var b = north.getBox();
38484 var m = north.getMargins();
38485 b.width = w - (m.left+m.right);
38488 centerY = b.height + b.y + m.bottom;
38489 centerH -= centerY;
38490 north.updateBox(this.safeBox(b));
38492 if(south && south.isVisible()){
38493 var b = south.getBox();
38494 var m = south.getMargins();
38495 b.width = w - (m.left+m.right);
38497 var totalHeight = (b.height + m.top + m.bottom);
38498 b.y = h - totalHeight + m.top;
38499 centerH -= totalHeight;
38500 south.updateBox(this.safeBox(b));
38502 if(west && west.isVisible()){
38503 var b = west.getBox();
38504 var m = west.getMargins();
38505 b.height = centerH - (m.top+m.bottom);
38507 b.y = centerY + m.top;
38508 var totalWidth = (b.width + m.left + m.right);
38509 centerX += totalWidth;
38510 centerW -= totalWidth;
38511 west.updateBox(this.safeBox(b));
38513 if(east && east.isVisible()){
38514 var b = east.getBox();
38515 var m = east.getMargins();
38516 b.height = centerH - (m.top+m.bottom);
38517 var totalWidth = (b.width + m.left + m.right);
38518 b.x = w - totalWidth + m.left;
38519 b.y = centerY + m.top;
38520 centerW -= totalWidth;
38521 east.updateBox(this.safeBox(b));
38524 var m = center.getMargins();
38526 x: centerX + m.left,
38527 y: centerY + m.top,
38528 width: centerW - (m.left+m.right),
38529 height: centerH - (m.top+m.bottom)
38531 //if(this.hideOnLayout){
38532 //center.el.setStyle("display", "block");
38534 center.updateBox(this.safeBox(centerBox));
38537 this.fireEvent("layout", this);
38541 safeBox : function(box){
38542 box.width = Math.max(0, box.width);
38543 box.height = Math.max(0, box.height);
38548 * Adds a ContentPanel (or subclass) to this layout.
38549 * @param {String} target The target region key (north, south, east, west or center).
38550 * @param {Roo.ContentPanel} panel The panel to add
38551 * @return {Roo.ContentPanel} The added panel
38553 add : function(target, panel){
38555 target = target.toLowerCase();
38556 return this.regions[target].add(panel);
38560 * Remove a ContentPanel (or subclass) to this layout.
38561 * @param {String} target The target region key (north, south, east, west or center).
38562 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38563 * @return {Roo.ContentPanel} The removed panel
38565 remove : function(target, panel){
38566 target = target.toLowerCase();
38567 return this.regions[target].remove(panel);
38571 * Searches all regions for a panel with the specified id
38572 * @param {String} panelId
38573 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38575 findPanel : function(panelId){
38576 var rs = this.regions;
38577 for(var target in rs){
38578 if(typeof rs[target] != "function"){
38579 var p = rs[target].getPanel(panelId);
38589 * Searches all regions for a panel with the specified id and activates (shows) it.
38590 * @param {String/ContentPanel} panelId The panels id or the panel itself
38591 * @return {Roo.ContentPanel} The shown panel or null
38593 showPanel : function(panelId) {
38594 var rs = this.regions;
38595 for(var target in rs){
38596 var r = rs[target];
38597 if(typeof r != "function"){
38598 if(r.hasPanel(panelId)){
38599 return r.showPanel(panelId);
38607 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38608 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38611 restoreState : function(provider){
38613 provider = Roo.state.Manager;
38615 var sm = new Roo.LayoutStateManager();
38616 sm.init(this, provider);
38622 * Adds a xtype elements to the layout.
38626 xtype : 'ContentPanel',
38633 xtype : 'NestedLayoutPanel',
38639 items : [ ... list of content panels or nested layout panels.. ]
38643 * @param {Object} cfg Xtype definition of item to add.
38645 addxtype : function(cfg)
38647 // basically accepts a pannel...
38648 // can accept a layout region..!?!?
38649 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38652 // theory? children can only be panels??
38654 //if (!cfg.xtype.match(/Panel$/)) {
38659 if (typeof(cfg.region) == 'undefined') {
38660 Roo.log("Failed to add Panel, region was not set");
38664 var region = cfg.region;
38670 xitems = cfg.items;
38675 if ( region == 'center') {
38676 Roo.log("Center: " + cfg.title);
38682 case 'Content': // ContentPanel (el, cfg)
38683 case 'Scroll': // ContentPanel (el, cfg)
38685 cfg.autoCreate = cfg.autoCreate || true;
38686 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38688 // var el = this.el.createChild();
38689 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38692 this.add(region, ret);
38696 case 'TreePanel': // our new panel!
38697 cfg.el = this.el.createChild();
38698 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38699 this.add(region, ret);
38704 // create a new Layout (which is a Border Layout...
38706 var clayout = cfg.layout;
38707 clayout.el = this.el.createChild();
38708 clayout.items = clayout.items || [];
38712 // replace this exitems with the clayout ones..
38713 xitems = clayout.items;
38715 // force background off if it's in center...
38716 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38717 cfg.background = false;
38719 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38722 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38723 //console.log('adding nested layout panel ' + cfg.toSource());
38724 this.add(region, ret);
38725 nb = {}; /// find first...
38730 // needs grid and region
38732 //var el = this.getRegion(region).el.createChild();
38734 *var el = this.el.createChild();
38735 // create the grid first...
38736 cfg.grid.container = el;
38737 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38740 if (region == 'center' && this.active ) {
38741 cfg.background = false;
38744 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38746 this.add(region, ret);
38748 if (cfg.background) {
38749 // render grid on panel activation (if panel background)
38750 ret.on('activate', function(gp) {
38751 if (!gp.grid.rendered) {
38752 // gp.grid.render(el);
38756 // cfg.grid.render(el);
38762 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38763 // it was the old xcomponent building that caused this before.
38764 // espeically if border is the top element in the tree.
38774 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38776 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38777 this.add(region, ret);
38781 throw "Can not add '" + cfg.xtype + "' to Border";
38787 this.beginUpdate();
38791 Roo.each(xitems, function(i) {
38792 region = nb && i.region ? i.region : false;
38794 var add = ret.addxtype(i);
38797 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38798 if (!i.background) {
38799 abn[region] = nb[region] ;
38806 // make the last non-background panel active..
38807 //if (nb) { Roo.log(abn); }
38810 for(var r in abn) {
38811 region = this.getRegion(r);
38813 // tried using nb[r], but it does not work..
38815 region.showPanel(abn[r]);
38826 factory : function(cfg)
38829 var validRegions = Roo.bootstrap.layout.Border.regions;
38831 var target = cfg.region;
38834 var r = Roo.bootstrap.layout;
38838 return new r.North(cfg);
38840 return new r.South(cfg);
38842 return new r.East(cfg);
38844 return new r.West(cfg);
38846 return new r.Center(cfg);
38848 throw 'Layout region "'+target+'" not supported.';
38855 * Ext JS Library 1.1.1
38856 * Copyright(c) 2006-2007, Ext JS, LLC.
38858 * Originally Released Under LGPL - original licence link has changed is not relivant.
38861 * <script type="text/javascript">
38865 * @class Roo.bootstrap.layout.Basic
38866 * @extends Roo.util.Observable
38867 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38868 * and does not have a titlebar, tabs or any other features. All it does is size and position
38869 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38870 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38871 * @cfg {string} region the region that it inhabits..
38872 * @cfg {bool} skipConfig skip config?
38876 Roo.bootstrap.layout.Basic = function(config){
38878 this.mgr = config.mgr;
38880 this.position = config.region;
38882 var skipConfig = config.skipConfig;
38886 * @scope Roo.BasicLayoutRegion
38890 * @event beforeremove
38891 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38892 * @param {Roo.LayoutRegion} this
38893 * @param {Roo.ContentPanel} panel The panel
38894 * @param {Object} e The cancel event object
38896 "beforeremove" : true,
38898 * @event invalidated
38899 * Fires when the layout for this region is changed.
38900 * @param {Roo.LayoutRegion} this
38902 "invalidated" : true,
38904 * @event visibilitychange
38905 * Fires when this region is shown or hidden
38906 * @param {Roo.LayoutRegion} this
38907 * @param {Boolean} visibility true or false
38909 "visibilitychange" : true,
38911 * @event paneladded
38912 * Fires when a panel is added.
38913 * @param {Roo.LayoutRegion} this
38914 * @param {Roo.ContentPanel} panel The panel
38916 "paneladded" : true,
38918 * @event panelremoved
38919 * Fires when a panel is removed.
38920 * @param {Roo.LayoutRegion} this
38921 * @param {Roo.ContentPanel} panel The panel
38923 "panelremoved" : true,
38925 * @event beforecollapse
38926 * Fires when this region before collapse.
38927 * @param {Roo.LayoutRegion} this
38929 "beforecollapse" : true,
38932 * Fires when this region is collapsed.
38933 * @param {Roo.LayoutRegion} this
38935 "collapsed" : true,
38938 * Fires when this region is expanded.
38939 * @param {Roo.LayoutRegion} this
38944 * Fires when this region is slid into view.
38945 * @param {Roo.LayoutRegion} this
38947 "slideshow" : true,
38950 * Fires when this region slides out of view.
38951 * @param {Roo.LayoutRegion} this
38953 "slidehide" : true,
38955 * @event panelactivated
38956 * Fires when a panel is activated.
38957 * @param {Roo.LayoutRegion} this
38958 * @param {Roo.ContentPanel} panel The activated panel
38960 "panelactivated" : true,
38963 * Fires when the user resizes this region.
38964 * @param {Roo.LayoutRegion} this
38965 * @param {Number} newSize The new size (width for east/west, height for north/south)
38969 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38970 this.panels = new Roo.util.MixedCollection();
38971 this.panels.getKey = this.getPanelId.createDelegate(this);
38973 this.activePanel = null;
38974 // ensure listeners are added...
38976 if (config.listeners || config.events) {
38977 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38978 listeners : config.listeners || {},
38979 events : config.events || {}
38983 if(skipConfig !== true){
38984 this.applyConfig(config);
38988 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38990 getPanelId : function(p){
38994 applyConfig : function(config){
38995 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38996 this.config = config;
39001 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
39002 * the width, for horizontal (north, south) the height.
39003 * @param {Number} newSize The new width or height
39005 resizeTo : function(newSize){
39006 var el = this.el ? this.el :
39007 (this.activePanel ? this.activePanel.getEl() : null);
39009 switch(this.position){
39012 el.setWidth(newSize);
39013 this.fireEvent("resized", this, newSize);
39017 el.setHeight(newSize);
39018 this.fireEvent("resized", this, newSize);
39024 getBox : function(){
39025 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39028 getMargins : function(){
39029 return this.margins;
39032 updateBox : function(box){
39034 var el = this.activePanel.getEl();
39035 el.dom.style.left = box.x + "px";
39036 el.dom.style.top = box.y + "px";
39037 this.activePanel.setSize(box.width, box.height);
39041 * Returns the container element for this region.
39042 * @return {Roo.Element}
39044 getEl : function(){
39045 return this.activePanel;
39049 * Returns true if this region is currently visible.
39050 * @return {Boolean}
39052 isVisible : function(){
39053 return this.activePanel ? true : false;
39056 setActivePanel : function(panel){
39057 panel = this.getPanel(panel);
39058 if(this.activePanel && this.activePanel != panel){
39059 this.activePanel.setActiveState(false);
39060 this.activePanel.getEl().setLeftTop(-10000,-10000);
39062 this.activePanel = panel;
39063 panel.setActiveState(true);
39065 panel.setSize(this.box.width, this.box.height);
39067 this.fireEvent("panelactivated", this, panel);
39068 this.fireEvent("invalidated");
39072 * Show the specified panel.
39073 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39074 * @return {Roo.ContentPanel} The shown panel or null
39076 showPanel : function(panel){
39077 panel = this.getPanel(panel);
39079 this.setActivePanel(panel);
39085 * Get the active panel for this region.
39086 * @return {Roo.ContentPanel} The active panel or null
39088 getActivePanel : function(){
39089 return this.activePanel;
39093 * Add the passed ContentPanel(s)
39094 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39095 * @return {Roo.ContentPanel} The panel added (if only one was added)
39097 add : function(panel){
39098 if(arguments.length > 1){
39099 for(var i = 0, len = arguments.length; i < len; i++) {
39100 this.add(arguments[i]);
39104 if(this.hasPanel(panel)){
39105 this.showPanel(panel);
39108 var el = panel.getEl();
39109 if(el.dom.parentNode != this.mgr.el.dom){
39110 this.mgr.el.dom.appendChild(el.dom);
39112 if(panel.setRegion){
39113 panel.setRegion(this);
39115 this.panels.add(panel);
39116 el.setStyle("position", "absolute");
39117 if(!panel.background){
39118 this.setActivePanel(panel);
39119 if(this.config.initialSize && this.panels.getCount()==1){
39120 this.resizeTo(this.config.initialSize);
39123 this.fireEvent("paneladded", this, panel);
39128 * Returns true if the panel is in this region.
39129 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39130 * @return {Boolean}
39132 hasPanel : function(panel){
39133 if(typeof panel == "object"){ // must be panel obj
39134 panel = panel.getId();
39136 return this.getPanel(panel) ? true : false;
39140 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39141 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39142 * @param {Boolean} preservePanel Overrides the config preservePanel option
39143 * @return {Roo.ContentPanel} The panel that was removed
39145 remove : function(panel, preservePanel){
39146 panel = this.getPanel(panel);
39151 this.fireEvent("beforeremove", this, panel, e);
39152 if(e.cancel === true){
39155 var panelId = panel.getId();
39156 this.panels.removeKey(panelId);
39161 * Returns the panel specified or null if it's not in this region.
39162 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39163 * @return {Roo.ContentPanel}
39165 getPanel : function(id){
39166 if(typeof id == "object"){ // must be panel obj
39169 return this.panels.get(id);
39173 * Returns this regions position (north/south/east/west/center).
39176 getPosition: function(){
39177 return this.position;
39181 * Ext JS Library 1.1.1
39182 * Copyright(c) 2006-2007, Ext JS, LLC.
39184 * Originally Released Under LGPL - original licence link has changed is not relivant.
39187 * <script type="text/javascript">
39191 * @class Roo.bootstrap.layout.Region
39192 * @extends Roo.bootstrap.layout.Basic
39193 * This class represents a region in a layout manager.
39195 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39196 * @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})
39197 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
39198 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
39199 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
39200 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
39201 * @cfg {String} title The title for the region (overrides panel titles)
39202 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
39203 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39204 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
39205 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39206 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
39207 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39208 * the space available, similar to FireFox 1.5 tabs (defaults to false)
39209 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
39210 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
39211 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
39213 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
39214 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
39215 * @cfg {Boolean} disableTabTips True to disable tab tooltips
39216 * @cfg {Number} width For East/West panels
39217 * @cfg {Number} height For North/South panels
39218 * @cfg {Boolean} split To show the splitter
39219 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
39221 * @cfg {string} cls Extra CSS classes to add to region
39223 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
39224 * @cfg {string} region the region that it inhabits..
39227 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
39228 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
39230 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
39231 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
39232 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
39234 Roo.bootstrap.layout.Region = function(config)
39236 this.applyConfig(config);
39238 var mgr = config.mgr;
39239 var pos = config.region;
39240 config.skipConfig = true;
39241 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39244 this.onRender(mgr.el);
39247 this.visible = true;
39248 this.collapsed = false;
39249 this.unrendered_panels = [];
39252 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39254 position: '', // set by wrapper (eg. north/south etc..)
39255 unrendered_panels : null, // unrendered panels.
39257 tabPosition : false,
39259 mgr: false, // points to 'Border'
39262 createBody : function(){
39263 /** This region's body element
39264 * @type Roo.Element */
39265 this.bodyEl = this.el.createChild({
39267 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39271 onRender: function(ctr, pos)
39273 var dh = Roo.DomHelper;
39274 /** This region's container element
39275 * @type Roo.Element */
39276 this.el = dh.append(ctr.dom, {
39278 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39280 /** This region's title element
39281 * @type Roo.Element */
39283 this.titleEl = dh.append(this.el.dom, {
39285 unselectable: "on",
39286 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39288 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
39289 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39293 this.titleEl.enableDisplayMode();
39294 /** This region's title text element
39295 * @type HTMLElement */
39296 this.titleTextEl = this.titleEl.dom.firstChild;
39297 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39299 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39300 this.closeBtn.enableDisplayMode();
39301 this.closeBtn.on("click", this.closeClicked, this);
39302 this.closeBtn.hide();
39304 this.createBody(this.config);
39305 if(this.config.hideWhenEmpty){
39307 this.on("paneladded", this.validateVisibility, this);
39308 this.on("panelremoved", this.validateVisibility, this);
39310 if(this.autoScroll){
39311 this.bodyEl.setStyle("overflow", "auto");
39313 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39315 //if(c.titlebar !== false){
39316 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39317 this.titleEl.hide();
39319 this.titleEl.show();
39320 if(this.config.title){
39321 this.titleTextEl.innerHTML = this.config.title;
39325 if(this.config.collapsed){
39326 this.collapse(true);
39328 if(this.config.hidden){
39332 if (this.unrendered_panels && this.unrendered_panels.length) {
39333 for (var i =0;i< this.unrendered_panels.length; i++) {
39334 this.add(this.unrendered_panels[i]);
39336 this.unrendered_panels = null;
39342 applyConfig : function(c)
39345 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39346 var dh = Roo.DomHelper;
39347 if(c.titlebar !== false){
39348 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39349 this.collapseBtn.on("click", this.collapse, this);
39350 this.collapseBtn.enableDisplayMode();
39352 if(c.showPin === true || this.showPin){
39353 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39354 this.stickBtn.enableDisplayMode();
39355 this.stickBtn.on("click", this.expand, this);
39356 this.stickBtn.hide();
39361 /** This region's collapsed element
39362 * @type Roo.Element */
39365 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39366 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39369 if(c.floatable !== false){
39370 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39371 this.collapsedEl.on("click", this.collapseClick, this);
39374 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39375 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39376 id: "message", unselectable: "on", style:{"float":"left"}});
39377 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39379 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39380 this.expandBtn.on("click", this.expand, this);
39384 if(this.collapseBtn){
39385 this.collapseBtn.setVisible(c.collapsible == true);
39388 this.cmargins = c.cmargins || this.cmargins ||
39389 (this.position == "west" || this.position == "east" ?
39390 {top: 0, left: 2, right:2, bottom: 0} :
39391 {top: 2, left: 0, right:0, bottom: 2});
39393 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39396 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39398 this.autoScroll = c.autoScroll || false;
39403 this.duration = c.duration || .30;
39404 this.slideDuration = c.slideDuration || .45;
39409 * Returns true if this region is currently visible.
39410 * @return {Boolean}
39412 isVisible : function(){
39413 return this.visible;
39417 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39418 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39420 //setCollapsedTitle : function(title){
39421 // title = title || " ";
39422 // if(this.collapsedTitleTextEl){
39423 // this.collapsedTitleTextEl.innerHTML = title;
39427 getBox : function(){
39429 // if(!this.collapsed){
39430 b = this.el.getBox(false, true);
39432 // b = this.collapsedEl.getBox(false, true);
39437 getMargins : function(){
39438 return this.margins;
39439 //return this.collapsed ? this.cmargins : this.margins;
39442 highlight : function(){
39443 this.el.addClass("x-layout-panel-dragover");
39446 unhighlight : function(){
39447 this.el.removeClass("x-layout-panel-dragover");
39450 updateBox : function(box)
39452 if (!this.bodyEl) {
39453 return; // not rendered yet..
39457 if(!this.collapsed){
39458 this.el.dom.style.left = box.x + "px";
39459 this.el.dom.style.top = box.y + "px";
39460 this.updateBody(box.width, box.height);
39462 this.collapsedEl.dom.style.left = box.x + "px";
39463 this.collapsedEl.dom.style.top = box.y + "px";
39464 this.collapsedEl.setSize(box.width, box.height);
39467 this.tabs.autoSizeTabs();
39471 updateBody : function(w, h)
39474 this.el.setWidth(w);
39475 w -= this.el.getBorderWidth("rl");
39476 if(this.config.adjustments){
39477 w += this.config.adjustments[0];
39480 if(h !== null && h > 0){
39481 this.el.setHeight(h);
39482 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39483 h -= this.el.getBorderWidth("tb");
39484 if(this.config.adjustments){
39485 h += this.config.adjustments[1];
39487 this.bodyEl.setHeight(h);
39489 h = this.tabs.syncHeight(h);
39492 if(this.panelSize){
39493 w = w !== null ? w : this.panelSize.width;
39494 h = h !== null ? h : this.panelSize.height;
39496 if(this.activePanel){
39497 var el = this.activePanel.getEl();
39498 w = w !== null ? w : el.getWidth();
39499 h = h !== null ? h : el.getHeight();
39500 this.panelSize = {width: w, height: h};
39501 this.activePanel.setSize(w, h);
39503 if(Roo.isIE && this.tabs){
39504 this.tabs.el.repaint();
39509 * Returns the container element for this region.
39510 * @return {Roo.Element}
39512 getEl : function(){
39517 * Hides this region.
39520 //if(!this.collapsed){
39521 this.el.dom.style.left = "-2000px";
39524 // this.collapsedEl.dom.style.left = "-2000px";
39525 // this.collapsedEl.hide();
39527 this.visible = false;
39528 this.fireEvent("visibilitychange", this, false);
39532 * Shows this region if it was previously hidden.
39535 //if(!this.collapsed){
39538 // this.collapsedEl.show();
39540 this.visible = true;
39541 this.fireEvent("visibilitychange", this, true);
39544 closeClicked : function(){
39545 if(this.activePanel){
39546 this.remove(this.activePanel);
39550 collapseClick : function(e){
39552 e.stopPropagation();
39555 e.stopPropagation();
39561 * Collapses this region.
39562 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39565 collapse : function(skipAnim, skipCheck = false){
39566 if(this.collapsed) {
39570 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39572 this.collapsed = true;
39574 this.split.el.hide();
39576 if(this.config.animate && skipAnim !== true){
39577 this.fireEvent("invalidated", this);
39578 this.animateCollapse();
39580 this.el.setLocation(-20000,-20000);
39582 this.collapsedEl.show();
39583 this.fireEvent("collapsed", this);
39584 this.fireEvent("invalidated", this);
39590 animateCollapse : function(){
39595 * Expands this region if it was previously collapsed.
39596 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39597 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39600 expand : function(e, skipAnim){
39602 e.stopPropagation();
39604 if(!this.collapsed || this.el.hasActiveFx()) {
39608 this.afterSlideIn();
39611 this.collapsed = false;
39612 if(this.config.animate && skipAnim !== true){
39613 this.animateExpand();
39617 this.split.el.show();
39619 this.collapsedEl.setLocation(-2000,-2000);
39620 this.collapsedEl.hide();
39621 this.fireEvent("invalidated", this);
39622 this.fireEvent("expanded", this);
39626 animateExpand : function(){
39630 initTabs : function()
39632 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39634 var ts = new Roo.bootstrap.panel.Tabs({
39635 el: this.bodyEl.dom,
39637 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39638 disableTooltips: this.config.disableTabTips,
39639 toolbar : this.config.toolbar
39642 if(this.config.hideTabs){
39643 ts.stripWrap.setDisplayed(false);
39646 ts.resizeTabs = this.config.resizeTabs === true;
39647 ts.minTabWidth = this.config.minTabWidth || 40;
39648 ts.maxTabWidth = this.config.maxTabWidth || 250;
39649 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39650 ts.monitorResize = false;
39651 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39652 ts.bodyEl.addClass('roo-layout-tabs-body');
39653 this.panels.each(this.initPanelAsTab, this);
39656 initPanelAsTab : function(panel){
39657 var ti = this.tabs.addTab(
39661 this.config.closeOnTab && panel.isClosable(),
39664 if(panel.tabTip !== undefined){
39665 ti.setTooltip(panel.tabTip);
39667 ti.on("activate", function(){
39668 this.setActivePanel(panel);
39671 if(this.config.closeOnTab){
39672 ti.on("beforeclose", function(t, e){
39674 this.remove(panel);
39678 panel.tabItem = ti;
39683 updatePanelTitle : function(panel, title)
39685 if(this.activePanel == panel){
39686 this.updateTitle(title);
39689 var ti = this.tabs.getTab(panel.getEl().id);
39691 if(panel.tabTip !== undefined){
39692 ti.setTooltip(panel.tabTip);
39697 updateTitle : function(title){
39698 if(this.titleTextEl && !this.config.title){
39699 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39703 setActivePanel : function(panel)
39705 panel = this.getPanel(panel);
39706 if(this.activePanel && this.activePanel != panel){
39707 if(this.activePanel.setActiveState(false) === false){
39711 this.activePanel = panel;
39712 panel.setActiveState(true);
39713 if(this.panelSize){
39714 panel.setSize(this.panelSize.width, this.panelSize.height);
39717 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39719 this.updateTitle(panel.getTitle());
39721 this.fireEvent("invalidated", this);
39723 this.fireEvent("panelactivated", this, panel);
39727 * Shows the specified panel.
39728 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39729 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39731 showPanel : function(panel)
39733 panel = this.getPanel(panel);
39736 var tab = this.tabs.getTab(panel.getEl().id);
39737 if(tab.isHidden()){
39738 this.tabs.unhideTab(tab.id);
39742 this.setActivePanel(panel);
39749 * Get the active panel for this region.
39750 * @return {Roo.ContentPanel} The active panel or null
39752 getActivePanel : function(){
39753 return this.activePanel;
39756 validateVisibility : function(){
39757 if(this.panels.getCount() < 1){
39758 this.updateTitle(" ");
39759 this.closeBtn.hide();
39762 if(!this.isVisible()){
39769 * Adds the passed ContentPanel(s) to this region.
39770 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39771 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39773 add : function(panel)
39775 if(arguments.length > 1){
39776 for(var i = 0, len = arguments.length; i < len; i++) {
39777 this.add(arguments[i]);
39782 // if we have not been rendered yet, then we can not really do much of this..
39783 if (!this.bodyEl) {
39784 this.unrendered_panels.push(panel);
39791 if(this.hasPanel(panel)){
39792 this.showPanel(panel);
39795 panel.setRegion(this);
39796 this.panels.add(panel);
39797 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39798 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39799 // and hide them... ???
39800 this.bodyEl.dom.appendChild(panel.getEl().dom);
39801 if(panel.background !== true){
39802 this.setActivePanel(panel);
39804 this.fireEvent("paneladded", this, panel);
39811 this.initPanelAsTab(panel);
39815 if(panel.background !== true){
39816 this.tabs.activate(panel.getEl().id);
39818 this.fireEvent("paneladded", this, panel);
39823 * Hides the tab for the specified panel.
39824 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39826 hidePanel : function(panel){
39827 if(this.tabs && (panel = this.getPanel(panel))){
39828 this.tabs.hideTab(panel.getEl().id);
39833 * Unhides the tab for a previously hidden panel.
39834 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39836 unhidePanel : function(panel){
39837 if(this.tabs && (panel = this.getPanel(panel))){
39838 this.tabs.unhideTab(panel.getEl().id);
39842 clearPanels : function(){
39843 while(this.panels.getCount() > 0){
39844 this.remove(this.panels.first());
39849 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39850 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39851 * @param {Boolean} preservePanel Overrides the config preservePanel option
39852 * @return {Roo.ContentPanel} The panel that was removed
39854 remove : function(panel, preservePanel)
39856 panel = this.getPanel(panel);
39861 this.fireEvent("beforeremove", this, panel, e);
39862 if(e.cancel === true){
39865 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39866 var panelId = panel.getId();
39867 this.panels.removeKey(panelId);
39869 document.body.appendChild(panel.getEl().dom);
39872 this.tabs.removeTab(panel.getEl().id);
39873 }else if (!preservePanel){
39874 this.bodyEl.dom.removeChild(panel.getEl().dom);
39876 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39877 var p = this.panels.first();
39878 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39879 tempEl.appendChild(p.getEl().dom);
39880 this.bodyEl.update("");
39881 this.bodyEl.dom.appendChild(p.getEl().dom);
39883 this.updateTitle(p.getTitle());
39885 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39886 this.setActivePanel(p);
39888 panel.setRegion(null);
39889 if(this.activePanel == panel){
39890 this.activePanel = null;
39892 if(this.config.autoDestroy !== false && preservePanel !== true){
39893 try{panel.destroy();}catch(e){}
39895 this.fireEvent("panelremoved", this, panel);
39900 * Returns the TabPanel component used by this region
39901 * @return {Roo.TabPanel}
39903 getTabs : function(){
39907 createTool : function(parentEl, className){
39908 var btn = Roo.DomHelper.append(parentEl, {
39910 cls: "x-layout-tools-button",
39913 cls: "roo-layout-tools-button-inner " + className,
39917 btn.addClassOnOver("roo-layout-tools-button-over");
39922 * Ext JS Library 1.1.1
39923 * Copyright(c) 2006-2007, Ext JS, LLC.
39925 * Originally Released Under LGPL - original licence link has changed is not relivant.
39928 * <script type="text/javascript">
39934 * @class Roo.SplitLayoutRegion
39935 * @extends Roo.LayoutRegion
39936 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39938 Roo.bootstrap.layout.Split = function(config){
39939 this.cursor = config.cursor;
39940 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39943 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39945 splitTip : "Drag to resize.",
39946 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39947 useSplitTips : false,
39949 applyConfig : function(config){
39950 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39953 onRender : function(ctr,pos) {
39955 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39956 if(!this.config.split){
39961 var splitEl = Roo.DomHelper.append(ctr.dom, {
39963 id: this.el.id + "-split",
39964 cls: "roo-layout-split roo-layout-split-"+this.position,
39967 /** The SplitBar for this region
39968 * @type Roo.SplitBar */
39969 // does not exist yet...
39970 Roo.log([this.position, this.orientation]);
39972 this.split = new Roo.bootstrap.SplitBar({
39973 dragElement : splitEl,
39974 resizingElement: this.el,
39975 orientation : this.orientation
39978 this.split.on("moved", this.onSplitMove, this);
39979 this.split.useShim = this.config.useShim === true;
39980 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39981 if(this.useSplitTips){
39982 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39984 //if(config.collapsible){
39985 // this.split.el.on("dblclick", this.collapse, this);
39988 if(typeof this.config.minSize != "undefined"){
39989 this.split.minSize = this.config.minSize;
39991 if(typeof this.config.maxSize != "undefined"){
39992 this.split.maxSize = this.config.maxSize;
39994 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39995 this.hideSplitter();
40000 getHMaxSize : function(){
40001 var cmax = this.config.maxSize || 10000;
40002 var center = this.mgr.getRegion("center");
40003 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
40006 getVMaxSize : function(){
40007 var cmax = this.config.maxSize || 10000;
40008 var center = this.mgr.getRegion("center");
40009 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
40012 onSplitMove : function(split, newSize){
40013 this.fireEvent("resized", this, newSize);
40017 * Returns the {@link Roo.SplitBar} for this region.
40018 * @return {Roo.SplitBar}
40020 getSplitBar : function(){
40025 this.hideSplitter();
40026 Roo.bootstrap.layout.Split.superclass.hide.call(this);
40029 hideSplitter : function(){
40031 this.split.el.setLocation(-2000,-2000);
40032 this.split.el.hide();
40038 this.split.el.show();
40040 Roo.bootstrap.layout.Split.superclass.show.call(this);
40043 beforeSlide: function(){
40044 if(Roo.isGecko){// firefox overflow auto bug workaround
40045 this.bodyEl.clip();
40047 this.tabs.bodyEl.clip();
40049 if(this.activePanel){
40050 this.activePanel.getEl().clip();
40052 if(this.activePanel.beforeSlide){
40053 this.activePanel.beforeSlide();
40059 afterSlide : function(){
40060 if(Roo.isGecko){// firefox overflow auto bug workaround
40061 this.bodyEl.unclip();
40063 this.tabs.bodyEl.unclip();
40065 if(this.activePanel){
40066 this.activePanel.getEl().unclip();
40067 if(this.activePanel.afterSlide){
40068 this.activePanel.afterSlide();
40074 initAutoHide : function(){
40075 if(this.autoHide !== false){
40076 if(!this.autoHideHd){
40077 var st = new Roo.util.DelayedTask(this.slideIn, this);
40078 this.autoHideHd = {
40079 "mouseout": function(e){
40080 if(!e.within(this.el, true)){
40084 "mouseover" : function(e){
40090 this.el.on(this.autoHideHd);
40094 clearAutoHide : function(){
40095 if(this.autoHide !== false){
40096 this.el.un("mouseout", this.autoHideHd.mouseout);
40097 this.el.un("mouseover", this.autoHideHd.mouseover);
40101 clearMonitor : function(){
40102 Roo.get(document).un("click", this.slideInIf, this);
40105 // these names are backwards but not changed for compat
40106 slideOut : function(){
40107 if(this.isSlid || this.el.hasActiveFx()){
40110 this.isSlid = true;
40111 if(this.collapseBtn){
40112 this.collapseBtn.hide();
40114 this.closeBtnState = this.closeBtn.getStyle('display');
40115 this.closeBtn.hide();
40117 this.stickBtn.show();
40120 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40121 this.beforeSlide();
40122 this.el.setStyle("z-index", 10001);
40123 this.el.slideIn(this.getSlideAnchor(), {
40124 callback: function(){
40126 this.initAutoHide();
40127 Roo.get(document).on("click", this.slideInIf, this);
40128 this.fireEvent("slideshow", this);
40135 afterSlideIn : function(){
40136 this.clearAutoHide();
40137 this.isSlid = false;
40138 this.clearMonitor();
40139 this.el.setStyle("z-index", "");
40140 if(this.collapseBtn){
40141 this.collapseBtn.show();
40143 this.closeBtn.setStyle('display', this.closeBtnState);
40145 this.stickBtn.hide();
40147 this.fireEvent("slidehide", this);
40150 slideIn : function(cb){
40151 if(!this.isSlid || this.el.hasActiveFx()){
40155 this.isSlid = false;
40156 this.beforeSlide();
40157 this.el.slideOut(this.getSlideAnchor(), {
40158 callback: function(){
40159 this.el.setLeftTop(-10000, -10000);
40161 this.afterSlideIn();
40169 slideInIf : function(e){
40170 if(!e.within(this.el)){
40175 animateCollapse : function(){
40176 this.beforeSlide();
40177 this.el.setStyle("z-index", 20000);
40178 var anchor = this.getSlideAnchor();
40179 this.el.slideOut(anchor, {
40180 callback : function(){
40181 this.el.setStyle("z-index", "");
40182 this.collapsedEl.slideIn(anchor, {duration:.3});
40184 this.el.setLocation(-10000,-10000);
40186 this.fireEvent("collapsed", this);
40193 animateExpand : function(){
40194 this.beforeSlide();
40195 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40196 this.el.setStyle("z-index", 20000);
40197 this.collapsedEl.hide({
40200 this.el.slideIn(this.getSlideAnchor(), {
40201 callback : function(){
40202 this.el.setStyle("z-index", "");
40205 this.split.el.show();
40207 this.fireEvent("invalidated", this);
40208 this.fireEvent("expanded", this);
40236 getAnchor : function(){
40237 return this.anchors[this.position];
40240 getCollapseAnchor : function(){
40241 return this.canchors[this.position];
40244 getSlideAnchor : function(){
40245 return this.sanchors[this.position];
40248 getAlignAdj : function(){
40249 var cm = this.cmargins;
40250 switch(this.position){
40266 getExpandAdj : function(){
40267 var c = this.collapsedEl, cm = this.cmargins;
40268 switch(this.position){
40270 return [-(cm.right+c.getWidth()+cm.left), 0];
40273 return [cm.right+c.getWidth()+cm.left, 0];
40276 return [0, -(cm.top+cm.bottom+c.getHeight())];
40279 return [0, cm.top+cm.bottom+c.getHeight()];
40285 * Ext JS Library 1.1.1
40286 * Copyright(c) 2006-2007, Ext JS, LLC.
40288 * Originally Released Under LGPL - original licence link has changed is not relivant.
40291 * <script type="text/javascript">
40294 * These classes are private internal classes
40296 Roo.bootstrap.layout.Center = function(config){
40297 config.region = "center";
40298 Roo.bootstrap.layout.Region.call(this, config);
40299 this.visible = true;
40300 this.minWidth = config.minWidth || 20;
40301 this.minHeight = config.minHeight || 20;
40304 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40306 // center panel can't be hidden
40310 // center panel can't be hidden
40313 getMinWidth: function(){
40314 return this.minWidth;
40317 getMinHeight: function(){
40318 return this.minHeight;
40332 Roo.bootstrap.layout.North = function(config)
40334 config.region = 'north';
40335 config.cursor = 'n-resize';
40337 Roo.bootstrap.layout.Split.call(this, config);
40341 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40342 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40343 this.split.el.addClass("roo-layout-split-v");
40345 //var size = config.initialSize || config.height;
40346 //if(this.el && typeof size != "undefined"){
40347 // this.el.setHeight(size);
40350 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40352 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40355 onRender : function(ctr, pos)
40357 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40358 var size = this.config.initialSize || this.config.height;
40359 if(this.el && typeof size != "undefined"){
40360 this.el.setHeight(size);
40365 getBox : function(){
40366 if(this.collapsed){
40367 return this.collapsedEl.getBox();
40369 var box = this.el.getBox();
40371 box.height += this.split.el.getHeight();
40376 updateBox : function(box){
40377 if(this.split && !this.collapsed){
40378 box.height -= this.split.el.getHeight();
40379 this.split.el.setLeft(box.x);
40380 this.split.el.setTop(box.y+box.height);
40381 this.split.el.setWidth(box.width);
40383 if(this.collapsed){
40384 this.updateBody(box.width, null);
40386 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40394 Roo.bootstrap.layout.South = function(config){
40395 config.region = 'south';
40396 config.cursor = 's-resize';
40397 Roo.bootstrap.layout.Split.call(this, config);
40399 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40400 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40401 this.split.el.addClass("roo-layout-split-v");
40406 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40407 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40409 onRender : function(ctr, pos)
40411 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40412 var size = this.config.initialSize || this.config.height;
40413 if(this.el && typeof size != "undefined"){
40414 this.el.setHeight(size);
40419 getBox : function(){
40420 if(this.collapsed){
40421 return this.collapsedEl.getBox();
40423 var box = this.el.getBox();
40425 var sh = this.split.el.getHeight();
40432 updateBox : function(box){
40433 if(this.split && !this.collapsed){
40434 var sh = this.split.el.getHeight();
40437 this.split.el.setLeft(box.x);
40438 this.split.el.setTop(box.y-sh);
40439 this.split.el.setWidth(box.width);
40441 if(this.collapsed){
40442 this.updateBody(box.width, null);
40444 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40448 Roo.bootstrap.layout.East = function(config){
40449 config.region = "east";
40450 config.cursor = "e-resize";
40451 Roo.bootstrap.layout.Split.call(this, config);
40453 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40454 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40455 this.split.el.addClass("roo-layout-split-h");
40459 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40460 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40462 onRender : function(ctr, pos)
40464 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40465 var size = this.config.initialSize || this.config.width;
40466 if(this.el && typeof size != "undefined"){
40467 this.el.setWidth(size);
40472 getBox : function(){
40473 if(this.collapsed){
40474 return this.collapsedEl.getBox();
40476 var box = this.el.getBox();
40478 var sw = this.split.el.getWidth();
40485 updateBox : function(box){
40486 if(this.split && !this.collapsed){
40487 var sw = this.split.el.getWidth();
40489 this.split.el.setLeft(box.x);
40490 this.split.el.setTop(box.y);
40491 this.split.el.setHeight(box.height);
40494 if(this.collapsed){
40495 this.updateBody(null, box.height);
40497 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40501 Roo.bootstrap.layout.West = function(config){
40502 config.region = "west";
40503 config.cursor = "w-resize";
40505 Roo.bootstrap.layout.Split.call(this, config);
40507 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40508 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40509 this.split.el.addClass("roo-layout-split-h");
40513 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40514 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40516 onRender: function(ctr, pos)
40518 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40519 var size = this.config.initialSize || this.config.width;
40520 if(typeof size != "undefined"){
40521 this.el.setWidth(size);
40525 getBox : function(){
40526 if(this.collapsed){
40527 return this.collapsedEl.getBox();
40529 var box = this.el.getBox();
40530 if (box.width == 0) {
40531 box.width = this.config.width; // kludge?
40534 box.width += this.split.el.getWidth();
40539 updateBox : function(box){
40540 if(this.split && !this.collapsed){
40541 var sw = this.split.el.getWidth();
40543 this.split.el.setLeft(box.x+box.width);
40544 this.split.el.setTop(box.y);
40545 this.split.el.setHeight(box.height);
40547 if(this.collapsed){
40548 this.updateBody(null, box.height);
40550 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40552 });Roo.namespace("Roo.bootstrap.panel");/*
40554 * Ext JS Library 1.1.1
40555 * Copyright(c) 2006-2007, Ext JS, LLC.
40557 * Originally Released Under LGPL - original licence link has changed is not relivant.
40560 * <script type="text/javascript">
40563 * @class Roo.ContentPanel
40564 * @extends Roo.util.Observable
40566 * A basic ContentPanel element.
40567 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40568 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40569 * @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
40570 * @cfg {Boolean} closable True if the panel can be closed/removed
40571 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40572 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40573 * @cfg {Toolbar} toolbar A toolbar for this panel
40574 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40575 * @cfg {String} title The title for this panel
40576 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40577 * @cfg {String} url Calls {@link #setUrl} with this value
40578 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40579 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40580 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40581 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40582 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40583 * @cfg {Boolean} badges render the badges
40584 * @cfg {String} cls extra classes to use
40585 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40588 * Create a new ContentPanel.
40589 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40590 * @param {String/Object} config A string to set only the title or a config object
40591 * @param {String} content (optional) Set the HTML content for this panel
40592 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40594 Roo.bootstrap.panel.Content = function( config){
40596 this.tpl = config.tpl || false;
40598 var el = config.el;
40599 var content = config.content;
40601 if(config.autoCreate){ // xtype is available if this is called from factory
40604 this.el = Roo.get(el);
40605 if(!this.el && config && config.autoCreate){
40606 if(typeof config.autoCreate == "object"){
40607 if(!config.autoCreate.id){
40608 config.autoCreate.id = config.id||el;
40610 this.el = Roo.DomHelper.append(document.body,
40611 config.autoCreate, true);
40615 cls: (config.cls || '') +
40616 (config.background ? ' bg-' + config.background : '') +
40617 " roo-layout-inactive-content",
40620 if (config.iframe) {
40624 style : 'border: 0px',
40625 src : 'about:blank'
40631 elcfg.html = config.html;
40635 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40636 if (config.iframe) {
40637 this.iframeEl = this.el.select('iframe',true).first();
40642 this.closable = false;
40643 this.loaded = false;
40644 this.active = false;
40647 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40649 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40651 this.wrapEl = this.el; //this.el.wrap();
40653 if (config.toolbar.items) {
40654 ti = config.toolbar.items ;
40655 delete config.toolbar.items ;
40659 this.toolbar.render(this.wrapEl, 'before');
40660 for(var i =0;i < ti.length;i++) {
40661 // Roo.log(['add child', items[i]]);
40662 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40664 this.toolbar.items = nitems;
40665 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40666 delete config.toolbar;
40670 // xtype created footer. - not sure if will work as we normally have to render first..
40671 if (this.footer && !this.footer.el && this.footer.xtype) {
40672 if (!this.wrapEl) {
40673 this.wrapEl = this.el.wrap();
40676 this.footer.container = this.wrapEl.createChild();
40678 this.footer = Roo.factory(this.footer, Roo);
40683 if(typeof config == "string"){
40684 this.title = config;
40686 Roo.apply(this, config);
40690 this.resizeEl = Roo.get(this.resizeEl, true);
40692 this.resizeEl = this.el;
40694 // handle view.xtype
40702 * Fires when this panel is activated.
40703 * @param {Roo.ContentPanel} this
40707 * @event deactivate
40708 * Fires when this panel is activated.
40709 * @param {Roo.ContentPanel} this
40711 "deactivate" : true,
40715 * Fires when this panel is resized if fitToFrame is true.
40716 * @param {Roo.ContentPanel} this
40717 * @param {Number} width The width after any component adjustments
40718 * @param {Number} height The height after any component adjustments
40724 * Fires when this tab is created
40725 * @param {Roo.ContentPanel} this
40731 * Fires when this content is scrolled
40732 * @param {Roo.ContentPanel} this
40733 * @param {Event} scrollEvent
40744 if(this.autoScroll && !this.iframe){
40745 this.resizeEl.setStyle("overflow", "auto");
40746 this.resizeEl.on('scroll', this.onScroll, this);
40748 // fix randome scrolling
40749 //this.el.on('scroll', function() {
40750 // Roo.log('fix random scolling');
40751 // this.scrollTo('top',0);
40754 content = content || this.content;
40756 this.setContent(content);
40758 if(config && config.url){
40759 this.setUrl(this.url, this.params, this.loadOnce);
40764 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40766 if (this.view && typeof(this.view.xtype) != 'undefined') {
40767 this.view.el = this.el.appendChild(document.createElement("div"));
40768 this.view = Roo.factory(this.view);
40769 this.view.render && this.view.render(false, '');
40773 this.fireEvent('render', this);
40776 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40786 /* Resize Element - use this to work out scroll etc. */
40789 setRegion : function(region){
40790 this.region = region;
40791 this.setActiveClass(region && !this.background);
40795 setActiveClass: function(state)
40798 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40799 this.el.setStyle('position','relative');
40801 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40802 this.el.setStyle('position', 'absolute');
40807 * Returns the toolbar for this Panel if one was configured.
40808 * @return {Roo.Toolbar}
40810 getToolbar : function(){
40811 return this.toolbar;
40814 setActiveState : function(active)
40816 this.active = active;
40817 this.setActiveClass(active);
40819 if(this.fireEvent("deactivate", this) === false){
40824 this.fireEvent("activate", this);
40828 * Updates this panel's element (not for iframe)
40829 * @param {String} content The new content
40830 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40832 setContent : function(content, loadScripts){
40837 this.el.update(content, loadScripts);
40840 ignoreResize : function(w, h){
40841 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40844 this.lastSize = {width: w, height: h};
40849 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40850 * @return {Roo.UpdateManager} The UpdateManager
40852 getUpdateManager : function(){
40856 return this.el.getUpdateManager();
40859 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40860 * Does not work with IFRAME contents
40861 * @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:
40864 url: "your-url.php",
40865 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40866 callback: yourFunction,
40867 scope: yourObject, //(optional scope)
40870 text: "Loading...",
40876 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40877 * 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.
40878 * @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}
40879 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40880 * @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.
40881 * @return {Roo.ContentPanel} this
40889 var um = this.el.getUpdateManager();
40890 um.update.apply(um, arguments);
40896 * 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.
40897 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40898 * @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)
40899 * @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)
40900 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40902 setUrl : function(url, params, loadOnce){
40904 this.iframeEl.dom.src = url;
40908 if(this.refreshDelegate){
40909 this.removeListener("activate", this.refreshDelegate);
40911 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40912 this.on("activate", this.refreshDelegate);
40913 return this.el.getUpdateManager();
40916 _handleRefresh : function(url, params, loadOnce){
40917 if(!loadOnce || !this.loaded){
40918 var updater = this.el.getUpdateManager();
40919 updater.update(url, params, this._setLoaded.createDelegate(this));
40923 _setLoaded : function(){
40924 this.loaded = true;
40928 * Returns this panel's id
40931 getId : function(){
40936 * Returns this panel's element - used by regiosn to add.
40937 * @return {Roo.Element}
40939 getEl : function(){
40940 return this.wrapEl || this.el;
40945 adjustForComponents : function(width, height)
40947 //Roo.log('adjustForComponents ');
40948 if(this.resizeEl != this.el){
40949 width -= this.el.getFrameWidth('lr');
40950 height -= this.el.getFrameWidth('tb');
40953 var te = this.toolbar.getEl();
40954 te.setWidth(width);
40955 height -= te.getHeight();
40958 var te = this.footer.getEl();
40959 te.setWidth(width);
40960 height -= te.getHeight();
40964 if(this.adjustments){
40965 width += this.adjustments[0];
40966 height += this.adjustments[1];
40968 return {"width": width, "height": height};
40971 setSize : function(width, height){
40972 if(this.fitToFrame && !this.ignoreResize(width, height)){
40973 if(this.fitContainer && this.resizeEl != this.el){
40974 this.el.setSize(width, height);
40976 var size = this.adjustForComponents(width, height);
40978 this.iframeEl.setSize(width,height);
40981 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40982 this.fireEvent('resize', this, size.width, size.height);
40989 * Returns this panel's title
40992 getTitle : function(){
40994 if (typeof(this.title) != 'object') {
40999 for (var k in this.title) {
41000 if (!this.title.hasOwnProperty(k)) {
41004 if (k.indexOf('-') >= 0) {
41005 var s = k.split('-');
41006 for (var i = 0; i<s.length; i++) {
41007 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
41010 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
41017 * Set this panel's title
41018 * @param {String} title
41020 setTitle : function(title){
41021 this.title = title;
41023 this.region.updatePanelTitle(this, title);
41028 * Returns true is this panel was configured to be closable
41029 * @return {Boolean}
41031 isClosable : function(){
41032 return this.closable;
41035 beforeSlide : function(){
41037 this.resizeEl.clip();
41040 afterSlide : function(){
41042 this.resizeEl.unclip();
41046 * Force a content refresh from the URL specified in the {@link #setUrl} method.
41047 * Will fail silently if the {@link #setUrl} method has not been called.
41048 * This does not activate the panel, just updates its content.
41050 refresh : function(){
41051 if(this.refreshDelegate){
41052 this.loaded = false;
41053 this.refreshDelegate();
41058 * Destroys this panel
41060 destroy : function(){
41061 this.el.removeAllListeners();
41062 var tempEl = document.createElement("span");
41063 tempEl.appendChild(this.el.dom);
41064 tempEl.innerHTML = "";
41070 * form - if the content panel contains a form - this is a reference to it.
41071 * @type {Roo.form.Form}
41075 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41076 * This contains a reference to it.
41082 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41092 * @param {Object} cfg Xtype definition of item to add.
41096 getChildContainer: function () {
41097 return this.getEl();
41101 onScroll : function(e)
41103 this.fireEvent('scroll', this, e);
41108 var ret = new Roo.factory(cfg);
41113 if (cfg.xtype.match(/^Form$/)) {
41116 //if (this.footer) {
41117 // el = this.footer.container.insertSibling(false, 'before');
41119 el = this.el.createChild();
41122 this.form = new Roo.form.Form(cfg);
41125 if ( this.form.allItems.length) {
41126 this.form.render(el.dom);
41130 // should only have one of theses..
41131 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41132 // views.. should not be just added - used named prop 'view''
41134 cfg.el = this.el.appendChild(document.createElement("div"));
41137 var ret = new Roo.factory(cfg);
41139 ret.render && ret.render(false, ''); // render blank..
41149 * @class Roo.bootstrap.panel.Grid
41150 * @extends Roo.bootstrap.panel.Content
41152 * Create a new GridPanel.
41153 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41154 * @param {Object} config A the config object
41160 Roo.bootstrap.panel.Grid = function(config)
41164 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41165 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41167 config.el = this.wrapper;
41168 //this.el = this.wrapper;
41170 if (config.container) {
41171 // ctor'ed from a Border/panel.grid
41174 this.wrapper.setStyle("overflow", "hidden");
41175 this.wrapper.addClass('roo-grid-container');
41180 if(config.toolbar){
41181 var tool_el = this.wrapper.createChild();
41182 this.toolbar = Roo.factory(config.toolbar);
41184 if (config.toolbar.items) {
41185 ti = config.toolbar.items ;
41186 delete config.toolbar.items ;
41190 this.toolbar.render(tool_el);
41191 for(var i =0;i < ti.length;i++) {
41192 // Roo.log(['add child', items[i]]);
41193 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41195 this.toolbar.items = nitems;
41197 delete config.toolbar;
41200 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41201 config.grid.scrollBody = true;;
41202 config.grid.monitorWindowResize = false; // turn off autosizing
41203 config.grid.autoHeight = false;
41204 config.grid.autoWidth = false;
41206 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41208 if (config.background) {
41209 // render grid on panel activation (if panel background)
41210 this.on('activate', function(gp) {
41211 if (!gp.grid.rendered) {
41212 gp.grid.render(this.wrapper);
41213 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41218 this.grid.render(this.wrapper);
41219 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41222 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41223 // ??? needed ??? config.el = this.wrapper;
41228 // xtype created footer. - not sure if will work as we normally have to render first..
41229 if (this.footer && !this.footer.el && this.footer.xtype) {
41231 var ctr = this.grid.getView().getFooterPanel(true);
41232 this.footer.dataSource = this.grid.dataSource;
41233 this.footer = Roo.factory(this.footer, Roo);
41234 this.footer.render(ctr);
41244 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41245 getId : function(){
41246 return this.grid.id;
41250 * Returns the grid for this panel
41251 * @return {Roo.bootstrap.Table}
41253 getGrid : function(){
41257 setSize : function(width, height){
41258 if(!this.ignoreResize(width, height)){
41259 var grid = this.grid;
41260 var size = this.adjustForComponents(width, height);
41261 // tfoot is not a footer?
41264 var gridel = grid.getGridEl();
41265 gridel.setSize(size.width, size.height);
41267 var tbd = grid.getGridEl().select('tbody', true).first();
41268 var thd = grid.getGridEl().select('thead',true).first();
41269 var tbf= grid.getGridEl().select('tfoot', true).first();
41272 size.height -= tbf.getHeight();
41275 size.height -= thd.getHeight();
41278 tbd.setSize(size.width, size.height );
41279 // this is for the account management tab -seems to work there.
41280 var thd = grid.getGridEl().select('thead',true).first();
41282 // tbd.setSize(size.width, size.height - thd.getHeight());
41291 beforeSlide : function(){
41292 this.grid.getView().scroller.clip();
41295 afterSlide : function(){
41296 this.grid.getView().scroller.unclip();
41299 destroy : function(){
41300 this.grid.destroy();
41302 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
41307 * @class Roo.bootstrap.panel.Nest
41308 * @extends Roo.bootstrap.panel.Content
41310 * Create a new Panel, that can contain a layout.Border.
41313 * @param {Roo.BorderLayout} layout The layout for this panel
41314 * @param {String/Object} config A string to set only the title or a config object
41316 Roo.bootstrap.panel.Nest = function(config)
41318 // construct with only one argument..
41319 /* FIXME - implement nicer consturctors
41320 if (layout.layout) {
41322 layout = config.layout;
41323 delete config.layout;
41325 if (layout.xtype && !layout.getEl) {
41326 // then layout needs constructing..
41327 layout = Roo.factory(layout, Roo);
41331 config.el = config.layout.getEl();
41333 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41335 config.layout.monitorWindowResize = false; // turn off autosizing
41336 this.layout = config.layout;
41337 this.layout.getEl().addClass("roo-layout-nested-layout");
41338 this.layout.parent = this;
41345 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41347 setSize : function(width, height){
41348 if(!this.ignoreResize(width, height)){
41349 var size = this.adjustForComponents(width, height);
41350 var el = this.layout.getEl();
41351 if (size.height < 1) {
41352 el.setWidth(size.width);
41354 el.setSize(size.width, size.height);
41356 var touch = el.dom.offsetWidth;
41357 this.layout.layout();
41358 // ie requires a double layout on the first pass
41359 if(Roo.isIE && !this.initialized){
41360 this.initialized = true;
41361 this.layout.layout();
41366 // activate all subpanels if not currently active..
41368 setActiveState : function(active){
41369 this.active = active;
41370 this.setActiveClass(active);
41373 this.fireEvent("deactivate", this);
41377 this.fireEvent("activate", this);
41378 // not sure if this should happen before or after..
41379 if (!this.layout) {
41380 return; // should not happen..
41383 for (var r in this.layout.regions) {
41384 reg = this.layout.getRegion(r);
41385 if (reg.getActivePanel()) {
41386 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41387 reg.setActivePanel(reg.getActivePanel());
41390 if (!reg.panels.length) {
41393 reg.showPanel(reg.getPanel(0));
41402 * Returns the nested BorderLayout for this panel
41403 * @return {Roo.BorderLayout}
41405 getLayout : function(){
41406 return this.layout;
41410 * Adds a xtype elements to the layout of the nested panel
41414 xtype : 'ContentPanel',
41421 xtype : 'NestedLayoutPanel',
41427 items : [ ... list of content panels or nested layout panels.. ]
41431 * @param {Object} cfg Xtype definition of item to add.
41433 addxtype : function(cfg) {
41434 return this.layout.addxtype(cfg);
41439 * Ext JS Library 1.1.1
41440 * Copyright(c) 2006-2007, Ext JS, LLC.
41442 * Originally Released Under LGPL - original licence link has changed is not relivant.
41445 * <script type="text/javascript">
41448 * @class Roo.TabPanel
41449 * @extends Roo.util.Observable
41450 * A lightweight tab container.
41454 // basic tabs 1, built from existing content
41455 var tabs = new Roo.TabPanel("tabs1");
41456 tabs.addTab("script", "View Script");
41457 tabs.addTab("markup", "View Markup");
41458 tabs.activate("script");
41460 // more advanced tabs, built from javascript
41461 var jtabs = new Roo.TabPanel("jtabs");
41462 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41464 // set up the UpdateManager
41465 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41466 var updater = tab2.getUpdateManager();
41467 updater.setDefaultUrl("ajax1.htm");
41468 tab2.on('activate', updater.refresh, updater, true);
41470 // Use setUrl for Ajax loading
41471 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41472 tab3.setUrl("ajax2.htm", null, true);
41475 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41478 jtabs.activate("jtabs-1");
41481 * Create a new TabPanel.
41482 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41483 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41485 Roo.bootstrap.panel.Tabs = function(config){
41487 * The container element for this TabPanel.
41488 * @type Roo.Element
41490 this.el = Roo.get(config.el);
41493 if(typeof config == "boolean"){
41494 this.tabPosition = config ? "bottom" : "top";
41496 Roo.apply(this, config);
41500 if(this.tabPosition == "bottom"){
41501 // if tabs are at the bottom = create the body first.
41502 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41503 this.el.addClass("roo-tabs-bottom");
41505 // next create the tabs holders
41507 if (this.tabPosition == "west"){
41509 var reg = this.region; // fake it..
41511 if (!reg.mgr.parent) {
41514 reg = reg.mgr.parent.region;
41516 Roo.log("got nest?");
41518 if (reg.mgr.getRegion('west')) {
41519 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41520 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41521 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41522 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41523 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41531 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41532 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41533 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41534 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41539 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41542 // finally - if tabs are at the top, then create the body last..
41543 if(this.tabPosition != "bottom"){
41544 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41545 * @type Roo.Element
41547 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41548 this.el.addClass("roo-tabs-top");
41552 this.bodyEl.setStyle("position", "relative");
41554 this.active = null;
41555 this.activateDelegate = this.activate.createDelegate(this);
41560 * Fires when the active tab changes
41561 * @param {Roo.TabPanel} this
41562 * @param {Roo.TabPanelItem} activePanel The new active tab
41566 * @event beforetabchange
41567 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41568 * @param {Roo.TabPanel} this
41569 * @param {Object} e Set cancel to true on this object to cancel the tab change
41570 * @param {Roo.TabPanelItem} tab The tab being changed to
41572 "beforetabchange" : true
41575 Roo.EventManager.onWindowResize(this.onResize, this);
41576 this.cpad = this.el.getPadding("lr");
41577 this.hiddenCount = 0;
41580 // toolbar on the tabbar support...
41581 if (this.toolbar) {
41582 alert("no toolbar support yet");
41583 this.toolbar = false;
41585 var tcfg = this.toolbar;
41586 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41587 this.toolbar = new Roo.Toolbar(tcfg);
41588 if (Roo.isSafari) {
41589 var tbl = tcfg.container.child('table', true);
41590 tbl.setAttribute('width', '100%');
41598 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41601 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41603 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41605 tabPosition : "top",
41607 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41609 currentTabWidth : 0,
41611 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41615 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41619 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41621 preferredTabWidth : 175,
41623 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41625 resizeTabs : false,
41627 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41629 monitorResize : true,
41631 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41633 toolbar : false, // set by caller..
41635 region : false, /// set by caller
41637 disableTooltips : true, // not used yet...
41640 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41641 * @param {String} id The id of the div to use <b>or create</b>
41642 * @param {String} text The text for the tab
41643 * @param {String} content (optional) Content to put in the TabPanelItem body
41644 * @param {Boolean} closable (optional) True to create a close icon on the tab
41645 * @return {Roo.TabPanelItem} The created TabPanelItem
41647 addTab : function(id, text, content, closable, tpl)
41649 var item = new Roo.bootstrap.panel.TabItem({
41653 closable : closable,
41656 this.addTabItem(item);
41658 item.setContent(content);
41664 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41665 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41666 * @return {Roo.TabPanelItem}
41668 getTab : function(id){
41669 return this.items[id];
41673 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41674 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41676 hideTab : function(id){
41677 var t = this.items[id];
41680 this.hiddenCount++;
41681 this.autoSizeTabs();
41686 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41687 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41689 unhideTab : function(id){
41690 var t = this.items[id];
41692 t.setHidden(false);
41693 this.hiddenCount--;
41694 this.autoSizeTabs();
41699 * Adds an existing {@link Roo.TabPanelItem}.
41700 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41702 addTabItem : function(item)
41704 this.items[item.id] = item;
41705 this.items.push(item);
41706 this.autoSizeTabs();
41707 // if(this.resizeTabs){
41708 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41709 // this.autoSizeTabs();
41711 // item.autoSize();
41716 * Removes a {@link Roo.TabPanelItem}.
41717 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41719 removeTab : function(id){
41720 var items = this.items;
41721 var tab = items[id];
41722 if(!tab) { return; }
41723 var index = items.indexOf(tab);
41724 if(this.active == tab && items.length > 1){
41725 var newTab = this.getNextAvailable(index);
41730 this.stripEl.dom.removeChild(tab.pnode.dom);
41731 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41732 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41734 items.splice(index, 1);
41735 delete this.items[tab.id];
41736 tab.fireEvent("close", tab);
41737 tab.purgeListeners();
41738 this.autoSizeTabs();
41741 getNextAvailable : function(start){
41742 var items = this.items;
41744 // look for a next tab that will slide over to
41745 // replace the one being removed
41746 while(index < items.length){
41747 var item = items[++index];
41748 if(item && !item.isHidden()){
41752 // if one isn't found select the previous tab (on the left)
41755 var item = items[--index];
41756 if(item && !item.isHidden()){
41764 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41765 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41767 disableTab : function(id){
41768 var tab = this.items[id];
41769 if(tab && this.active != tab){
41775 * Enables a {@link Roo.TabPanelItem} that is disabled.
41776 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41778 enableTab : function(id){
41779 var tab = this.items[id];
41784 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41785 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41786 * @return {Roo.TabPanelItem} The TabPanelItem.
41788 activate : function(id)
41790 //Roo.log('activite:' + id);
41792 var tab = this.items[id];
41796 if(tab == this.active || tab.disabled){
41800 this.fireEvent("beforetabchange", this, e, tab);
41801 if(e.cancel !== true && !tab.disabled){
41803 this.active.hide();
41805 this.active = this.items[id];
41806 this.active.show();
41807 this.fireEvent("tabchange", this, this.active);
41813 * Gets the active {@link Roo.TabPanelItem}.
41814 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41816 getActiveTab : function(){
41817 return this.active;
41821 * Updates the tab body element to fit the height of the container element
41822 * for overflow scrolling
41823 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41825 syncHeight : function(targetHeight){
41826 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41827 var bm = this.bodyEl.getMargins();
41828 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41829 this.bodyEl.setHeight(newHeight);
41833 onResize : function(){
41834 if(this.monitorResize){
41835 this.autoSizeTabs();
41840 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41842 beginUpdate : function(){
41843 this.updating = true;
41847 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41849 endUpdate : function(){
41850 this.updating = false;
41851 this.autoSizeTabs();
41855 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41857 autoSizeTabs : function()
41859 var count = this.items.length;
41860 var vcount = count - this.hiddenCount;
41863 this.stripEl.hide();
41865 this.stripEl.show();
41868 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41873 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41874 var availWidth = Math.floor(w / vcount);
41875 var b = this.stripBody;
41876 if(b.getWidth() > w){
41877 var tabs = this.items;
41878 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41879 if(availWidth < this.minTabWidth){
41880 /*if(!this.sleft){ // incomplete scrolling code
41881 this.createScrollButtons();
41884 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41887 if(this.currentTabWidth < this.preferredTabWidth){
41888 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41894 * Returns the number of tabs in this TabPanel.
41897 getCount : function(){
41898 return this.items.length;
41902 * Resizes all the tabs to the passed width
41903 * @param {Number} The new width
41905 setTabWidth : function(width){
41906 this.currentTabWidth = width;
41907 for(var i = 0, len = this.items.length; i < len; i++) {
41908 if(!this.items[i].isHidden()) {
41909 this.items[i].setWidth(width);
41915 * Destroys this TabPanel
41916 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41918 destroy : function(removeEl){
41919 Roo.EventManager.removeResizeListener(this.onResize, this);
41920 for(var i = 0, len = this.items.length; i < len; i++){
41921 this.items[i].purgeListeners();
41923 if(removeEl === true){
41924 this.el.update("");
41929 createStrip : function(container)
41931 var strip = document.createElement("nav");
41932 strip.className = Roo.bootstrap.version == 4 ?
41933 "navbar-light bg-light" :
41934 "navbar navbar-default"; //"x-tabs-wrap";
41935 container.appendChild(strip);
41939 createStripList : function(strip)
41941 // div wrapper for retard IE
41942 // returns the "tr" element.
41943 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41944 //'<div class="x-tabs-strip-wrap">'+
41945 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41946 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41947 return strip.firstChild; //.firstChild.firstChild.firstChild;
41949 createBody : function(container)
41951 var body = document.createElement("div");
41952 Roo.id(body, "tab-body");
41953 //Roo.fly(body).addClass("x-tabs-body");
41954 Roo.fly(body).addClass("tab-content");
41955 container.appendChild(body);
41958 createItemBody :function(bodyEl, id){
41959 var body = Roo.getDom(id);
41961 body = document.createElement("div");
41964 //Roo.fly(body).addClass("x-tabs-item-body");
41965 Roo.fly(body).addClass("tab-pane");
41966 bodyEl.insertBefore(body, bodyEl.firstChild);
41970 createStripElements : function(stripEl, text, closable, tpl)
41972 var td = document.createElement("li"); // was td..
41973 td.className = 'nav-item';
41975 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41978 stripEl.appendChild(td);
41980 td.className = "x-tabs-closable";
41981 if(!this.closeTpl){
41982 this.closeTpl = new Roo.Template(
41983 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41984 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41985 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41988 var el = this.closeTpl.overwrite(td, {"text": text});
41989 var close = el.getElementsByTagName("div")[0];
41990 var inner = el.getElementsByTagName("em")[0];
41991 return {"el": el, "close": close, "inner": inner};
41994 // not sure what this is..
41995 // if(!this.tabTpl){
41996 //this.tabTpl = new Roo.Template(
41997 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41998 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
42000 // this.tabTpl = new Roo.Template(
42001 // '<a href="#">' +
42002 // '<span unselectable="on"' +
42003 // (this.disableTooltips ? '' : ' title="{text}"') +
42004 // ' >{text}</span></a>'
42010 var template = tpl || this.tabTpl || false;
42013 template = new Roo.Template(
42014 Roo.bootstrap.version == 4 ?
42016 '<a class="nav-link" href="#" unselectable="on"' +
42017 (this.disableTooltips ? '' : ' title="{text}"') +
42020 '<a class="nav-link" href="#">' +
42021 '<span unselectable="on"' +
42022 (this.disableTooltips ? '' : ' title="{text}"') +
42023 ' >{text}</span></a>'
42028 switch (typeof(template)) {
42032 template = new Roo.Template(template);
42038 var el = template.overwrite(td, {"text": text});
42040 var inner = el.getElementsByTagName("span")[0];
42042 return {"el": el, "inner": inner};
42050 * @class Roo.TabPanelItem
42051 * @extends Roo.util.Observable
42052 * Represents an individual item (tab plus body) in a TabPanel.
42053 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42054 * @param {String} id The id of this TabPanelItem
42055 * @param {String} text The text for the tab of this TabPanelItem
42056 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42058 Roo.bootstrap.panel.TabItem = function(config){
42060 * The {@link Roo.TabPanel} this TabPanelItem belongs to
42061 * @type Roo.TabPanel
42063 this.tabPanel = config.panel;
42065 * The id for this TabPanelItem
42068 this.id = config.id;
42070 this.disabled = false;
42072 this.text = config.text;
42074 this.loaded = false;
42075 this.closable = config.closable;
42078 * The body element for this TabPanelItem.
42079 * @type Roo.Element
42081 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42082 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42083 this.bodyEl.setStyle("display", "block");
42084 this.bodyEl.setStyle("zoom", "1");
42085 //this.hideAction();
42087 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42089 this.el = Roo.get(els.el);
42090 this.inner = Roo.get(els.inner, true);
42091 this.textEl = Roo.bootstrap.version == 4 ?
42092 this.el : Roo.get(this.el.dom.firstChild, true);
42094 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42095 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42098 // this.el.on("mousedown", this.onTabMouseDown, this);
42099 this.el.on("click", this.onTabClick, this);
42101 if(config.closable){
42102 var c = Roo.get(els.close, true);
42103 c.dom.title = this.closeText;
42104 c.addClassOnOver("close-over");
42105 c.on("click", this.closeClick, this);
42111 * Fires when this tab becomes the active tab.
42112 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42113 * @param {Roo.TabPanelItem} this
42117 * @event beforeclose
42118 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42119 * @param {Roo.TabPanelItem} this
42120 * @param {Object} e Set cancel to true on this object to cancel the close.
42122 "beforeclose": true,
42125 * Fires when this tab is closed.
42126 * @param {Roo.TabPanelItem} this
42130 * @event deactivate
42131 * Fires when this tab is no longer the active tab.
42132 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42133 * @param {Roo.TabPanelItem} this
42135 "deactivate" : true
42137 this.hidden = false;
42139 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42142 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42144 purgeListeners : function(){
42145 Roo.util.Observable.prototype.purgeListeners.call(this);
42146 this.el.removeAllListeners();
42149 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42152 this.status_node.addClass("active");
42155 this.tabPanel.stripWrap.repaint();
42157 this.fireEvent("activate", this.tabPanel, this);
42161 * Returns true if this tab is the active tab.
42162 * @return {Boolean}
42164 isActive : function(){
42165 return this.tabPanel.getActiveTab() == this;
42169 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42172 this.status_node.removeClass("active");
42174 this.fireEvent("deactivate", this.tabPanel, this);
42177 hideAction : function(){
42178 this.bodyEl.hide();
42179 this.bodyEl.setStyle("position", "absolute");
42180 this.bodyEl.setLeft("-20000px");
42181 this.bodyEl.setTop("-20000px");
42184 showAction : function(){
42185 this.bodyEl.setStyle("position", "relative");
42186 this.bodyEl.setTop("");
42187 this.bodyEl.setLeft("");
42188 this.bodyEl.show();
42192 * Set the tooltip for the tab.
42193 * @param {String} tooltip The tab's tooltip
42195 setTooltip : function(text){
42196 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42197 this.textEl.dom.qtip = text;
42198 this.textEl.dom.removeAttribute('title');
42200 this.textEl.dom.title = text;
42204 onTabClick : function(e){
42205 e.preventDefault();
42206 this.tabPanel.activate(this.id);
42209 onTabMouseDown : function(e){
42210 e.preventDefault();
42211 this.tabPanel.activate(this.id);
42214 getWidth : function(){
42215 return this.inner.getWidth();
42218 setWidth : function(width){
42219 var iwidth = width - this.linode.getPadding("lr");
42220 this.inner.setWidth(iwidth);
42221 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42222 this.linode.setWidth(width);
42226 * Show or hide the tab
42227 * @param {Boolean} hidden True to hide or false to show.
42229 setHidden : function(hidden){
42230 this.hidden = hidden;
42231 this.linode.setStyle("display", hidden ? "none" : "");
42235 * Returns true if this tab is "hidden"
42236 * @return {Boolean}
42238 isHidden : function(){
42239 return this.hidden;
42243 * Returns the text for this tab
42246 getText : function(){
42250 autoSize : function(){
42251 //this.el.beginMeasure();
42252 this.textEl.setWidth(1);
42254 * #2804 [new] Tabs in Roojs
42255 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42257 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42258 //this.el.endMeasure();
42262 * Sets the text for the tab (Note: this also sets the tooltip text)
42263 * @param {String} text The tab's text and tooltip
42265 setText : function(text){
42267 this.textEl.update(text);
42268 this.setTooltip(text);
42269 //if(!this.tabPanel.resizeTabs){
42270 // this.autoSize();
42274 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42276 activate : function(){
42277 this.tabPanel.activate(this.id);
42281 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42283 disable : function(){
42284 if(this.tabPanel.active != this){
42285 this.disabled = true;
42286 this.status_node.addClass("disabled");
42291 * Enables this TabPanelItem if it was previously disabled.
42293 enable : function(){
42294 this.disabled = false;
42295 this.status_node.removeClass("disabled");
42299 * Sets the content for this TabPanelItem.
42300 * @param {String} content The content
42301 * @param {Boolean} loadScripts true to look for and load scripts
42303 setContent : function(content, loadScripts){
42304 this.bodyEl.update(content, loadScripts);
42308 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42309 * @return {Roo.UpdateManager} The UpdateManager
42311 getUpdateManager : function(){
42312 return this.bodyEl.getUpdateManager();
42316 * Set a URL to be used to load the content for this TabPanelItem.
42317 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42318 * @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)
42319 * @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)
42320 * @return {Roo.UpdateManager} The UpdateManager
42322 setUrl : function(url, params, loadOnce){
42323 if(this.refreshDelegate){
42324 this.un('activate', this.refreshDelegate);
42326 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42327 this.on("activate", this.refreshDelegate);
42328 return this.bodyEl.getUpdateManager();
42332 _handleRefresh : function(url, params, loadOnce){
42333 if(!loadOnce || !this.loaded){
42334 var updater = this.bodyEl.getUpdateManager();
42335 updater.update(url, params, this._setLoaded.createDelegate(this));
42340 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42341 * Will fail silently if the setUrl method has not been called.
42342 * This does not activate the panel, just updates its content.
42344 refresh : function(){
42345 if(this.refreshDelegate){
42346 this.loaded = false;
42347 this.refreshDelegate();
42352 _setLoaded : function(){
42353 this.loaded = true;
42357 closeClick : function(e){
42360 this.fireEvent("beforeclose", this, o);
42361 if(o.cancel !== true){
42362 this.tabPanel.removeTab(this.id);
42366 * The text displayed in the tooltip for the close icon.
42369 closeText : "Close this tab"
42372 * This script refer to:
42373 * Title: International Telephone Input
42374 * Author: Jack O'Connor
42375 * Code version: v12.1.12
42376 * Availability: https://github.com/jackocnr/intl-tel-input.git
42379 Roo.bootstrap.PhoneInputData = function() {
42382 "Afghanistan (افغانستان)",
42387 "Albania (Shqipëri)",
42392 "Algeria (الجزائر)",
42417 "Antigua and Barbuda",
42427 "Armenia (Հայաստան)",
42443 "Austria (Österreich)",
42448 "Azerbaijan (Azərbaycan)",
42458 "Bahrain (البحرين)",
42463 "Bangladesh (বাংলাদেশ)",
42473 "Belarus (Беларусь)",
42478 "Belgium (België)",
42508 "Bosnia and Herzegovina (Босна и Херцеговина)",
42523 "British Indian Ocean Territory",
42528 "British Virgin Islands",
42538 "Bulgaria (България)",
42548 "Burundi (Uburundi)",
42553 "Cambodia (កម្ពុជា)",
42558 "Cameroon (Cameroun)",
42567 ["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"]
42570 "Cape Verde (Kabu Verdi)",
42575 "Caribbean Netherlands",
42586 "Central African Republic (République centrafricaine)",
42606 "Christmas Island",
42612 "Cocos (Keeling) Islands",
42623 "Comoros (جزر القمر)",
42628 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42633 "Congo (Republic) (Congo-Brazzaville)",
42653 "Croatia (Hrvatska)",
42674 "Czech Republic (Česká republika)",
42679 "Denmark (Danmark)",
42694 "Dominican Republic (República Dominicana)",
42698 ["809", "829", "849"]
42716 "Equatorial Guinea (Guinea Ecuatorial)",
42736 "Falkland Islands (Islas Malvinas)",
42741 "Faroe Islands (Føroyar)",
42762 "French Guiana (Guyane française)",
42767 "French Polynesia (Polynésie française)",
42782 "Georgia (საქართველო)",
42787 "Germany (Deutschland)",
42807 "Greenland (Kalaallit Nunaat)",
42844 "Guinea-Bissau (Guiné Bissau)",
42869 "Hungary (Magyarország)",
42874 "Iceland (Ísland)",
42894 "Iraq (العراق)",
42910 "Israel (ישראל)",
42937 "Jordan (الأردن)",
42942 "Kazakhstan (Казахстан)",
42963 "Kuwait (الكويت)",
42968 "Kyrgyzstan (Кыргызстан)",
42978 "Latvia (Latvija)",
42983 "Lebanon (لبنان)",
42998 "Libya (ليبيا)",
43008 "Lithuania (Lietuva)",
43023 "Macedonia (FYROM) (Македонија)",
43028 "Madagascar (Madagasikara)",
43058 "Marshall Islands",
43068 "Mauritania (موريتانيا)",
43073 "Mauritius (Moris)",
43094 "Moldova (Republica Moldova)",
43104 "Mongolia (Монгол)",
43109 "Montenegro (Crna Gora)",
43119 "Morocco (المغرب)",
43125 "Mozambique (Moçambique)",
43130 "Myanmar (Burma) (မြန်မာ)",
43135 "Namibia (Namibië)",
43150 "Netherlands (Nederland)",
43155 "New Caledonia (Nouvelle-Calédonie)",
43190 "North Korea (조선 민주주의 인민 공화국)",
43195 "Northern Mariana Islands",
43211 "Pakistan (پاکستان)",
43221 "Palestine (فلسطين)",
43231 "Papua New Guinea",
43273 "Réunion (La Réunion)",
43279 "Romania (România)",
43295 "Saint Barthélemy",
43306 "Saint Kitts and Nevis",
43316 "Saint Martin (Saint-Martin (partie française))",
43322 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43327 "Saint Vincent and the Grenadines",
43342 "São Tomé and Príncipe (São Tomé e Príncipe)",
43347 "Saudi Arabia (المملكة العربية السعودية)",
43352 "Senegal (Sénégal)",
43382 "Slovakia (Slovensko)",
43387 "Slovenia (Slovenija)",
43397 "Somalia (Soomaaliya)",
43407 "South Korea (대한민국)",
43412 "South Sudan (جنوب السودان)",
43422 "Sri Lanka (ශ්රී ලංකාව)",
43427 "Sudan (السودان)",
43437 "Svalbard and Jan Mayen",
43448 "Sweden (Sverige)",
43453 "Switzerland (Schweiz)",
43458 "Syria (سوريا)",
43503 "Trinidad and Tobago",
43508 "Tunisia (تونس)",
43513 "Turkey (Türkiye)",
43523 "Turks and Caicos Islands",
43533 "U.S. Virgin Islands",
43543 "Ukraine (Україна)",
43548 "United Arab Emirates (الإمارات العربية المتحدة)",
43570 "Uzbekistan (Oʻzbekiston)",
43580 "Vatican City (Città del Vaticano)",
43591 "Vietnam (Việt Nam)",
43596 "Wallis and Futuna (Wallis-et-Futuna)",
43601 "Western Sahara (الصحراء الغربية)",
43607 "Yemen (اليمن)",
43631 * This script refer to:
43632 * Title: International Telephone Input
43633 * Author: Jack O'Connor
43634 * Code version: v12.1.12
43635 * Availability: https://github.com/jackocnr/intl-tel-input.git
43639 * @class Roo.bootstrap.PhoneInput
43640 * @extends Roo.bootstrap.TriggerField
43641 * An input with International dial-code selection
43643 * @cfg {String} defaultDialCode default '+852'
43644 * @cfg {Array} preferedCountries default []
43647 * Create a new PhoneInput.
43648 * @param {Object} config Configuration options
43651 Roo.bootstrap.PhoneInput = function(config) {
43652 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43655 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43657 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43659 listWidth: undefined,
43661 selectedClass: 'active',
43663 invalidClass : "has-warning",
43665 validClass: 'has-success',
43667 allowed: '0123456789',
43672 * @cfg {String} defaultDialCode The default dial code when initializing the input
43674 defaultDialCode: '+852',
43677 * @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
43679 preferedCountries: false,
43681 getAutoCreate : function()
43683 var data = Roo.bootstrap.PhoneInputData();
43684 var align = this.labelAlign || this.parentLabelAlign();
43687 this.allCountries = [];
43688 this.dialCodeMapping = [];
43690 for (var i = 0; i < data.length; i++) {
43692 this.allCountries[i] = {
43696 priority: c[3] || 0,
43697 areaCodes: c[4] || null
43699 this.dialCodeMapping[c[2]] = {
43702 priority: c[3] || 0,
43703 areaCodes: c[4] || null
43715 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43716 maxlength: this.max_length,
43717 cls : 'form-control tel-input',
43718 autocomplete: 'new-password'
43721 var hiddenInput = {
43724 cls: 'hidden-tel-input'
43728 hiddenInput.name = this.name;
43731 if (this.disabled) {
43732 input.disabled = true;
43735 var flag_container = {
43752 cls: this.hasFeedback ? 'has-feedback' : '',
43758 cls: 'dial-code-holder',
43765 cls: 'roo-select2-container input-group',
43772 if (this.fieldLabel.length) {
43775 tooltip: 'This field is required'
43781 cls: 'control-label',
43787 html: this.fieldLabel
43790 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43796 if(this.indicatorpos == 'right') {
43797 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43804 if(align == 'left') {
43812 if(this.labelWidth > 12){
43813 label.style = "width: " + this.labelWidth + 'px';
43815 if(this.labelWidth < 13 && this.labelmd == 0){
43816 this.labelmd = this.labelWidth;
43818 if(this.labellg > 0){
43819 label.cls += ' col-lg-' + this.labellg;
43820 input.cls += ' col-lg-' + (12 - this.labellg);
43822 if(this.labelmd > 0){
43823 label.cls += ' col-md-' + this.labelmd;
43824 container.cls += ' col-md-' + (12 - this.labelmd);
43826 if(this.labelsm > 0){
43827 label.cls += ' col-sm-' + this.labelsm;
43828 container.cls += ' col-sm-' + (12 - this.labelsm);
43830 if(this.labelxs > 0){
43831 label.cls += ' col-xs-' + this.labelxs;
43832 container.cls += ' col-xs-' + (12 - this.labelxs);
43842 var settings = this;
43844 ['xs','sm','md','lg'].map(function(size){
43845 if (settings[size]) {
43846 cfg.cls += ' col-' + size + '-' + settings[size];
43850 this.store = new Roo.data.Store({
43851 proxy : new Roo.data.MemoryProxy({}),
43852 reader : new Roo.data.JsonReader({
43863 'name' : 'dialCode',
43867 'name' : 'priority',
43871 'name' : 'areaCodes',
43878 if(!this.preferedCountries) {
43879 this.preferedCountries = [
43886 var p = this.preferedCountries.reverse();
43889 for (var i = 0; i < p.length; i++) {
43890 for (var j = 0; j < this.allCountries.length; j++) {
43891 if(this.allCountries[j].iso2 == p[i]) {
43892 var t = this.allCountries[j];
43893 this.allCountries.splice(j,1);
43894 this.allCountries.unshift(t);
43900 this.store.proxy.data = {
43902 data: this.allCountries
43908 initEvents : function()
43911 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43913 this.indicator = this.indicatorEl();
43914 this.flag = this.flagEl();
43915 this.dialCodeHolder = this.dialCodeHolderEl();
43917 this.trigger = this.el.select('div.flag-box',true).first();
43918 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43923 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43924 _this.list.setWidth(lw);
43927 this.list.on('mouseover', this.onViewOver, this);
43928 this.list.on('mousemove', this.onViewMove, this);
43929 this.inputEl().on("keyup", this.onKeyUp, this);
43930 this.inputEl().on("keypress", this.onKeyPress, this);
43932 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43934 this.view = new Roo.View(this.list, this.tpl, {
43935 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43938 this.view.on('click', this.onViewClick, this);
43939 this.setValue(this.defaultDialCode);
43942 onTriggerClick : function(e)
43944 Roo.log('trigger click');
43949 if(this.isExpanded()){
43951 this.hasFocus = false;
43953 this.store.load({});
43954 this.hasFocus = true;
43959 isExpanded : function()
43961 return this.list.isVisible();
43964 collapse : function()
43966 if(!this.isExpanded()){
43970 Roo.get(document).un('mousedown', this.collapseIf, this);
43971 Roo.get(document).un('mousewheel', this.collapseIf, this);
43972 this.fireEvent('collapse', this);
43976 expand : function()
43980 if(this.isExpanded() || !this.hasFocus){
43984 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43985 this.list.setWidth(lw);
43988 this.restrictHeight();
43990 Roo.get(document).on('mousedown', this.collapseIf, this);
43991 Roo.get(document).on('mousewheel', this.collapseIf, this);
43993 this.fireEvent('expand', this);
43996 restrictHeight : function()
43998 this.list.alignTo(this.inputEl(), this.listAlign);
43999 this.list.alignTo(this.inputEl(), this.listAlign);
44002 onViewOver : function(e, t)
44004 if(this.inKeyMode){
44007 var item = this.view.findItemFromChild(t);
44010 var index = this.view.indexOf(item);
44011 this.select(index, false);
44016 onViewClick : function(view, doFocus, el, e)
44018 var index = this.view.getSelectedIndexes()[0];
44020 var r = this.store.getAt(index);
44023 this.onSelect(r, index);
44025 if(doFocus !== false && !this.blockFocus){
44026 this.inputEl().focus();
44030 onViewMove : function(e, t)
44032 this.inKeyMode = false;
44035 select : function(index, scrollIntoView)
44037 this.selectedIndex = index;
44038 this.view.select(index);
44039 if(scrollIntoView !== false){
44040 var el = this.view.getNode(index);
44042 this.list.scrollChildIntoView(el, false);
44047 createList : function()
44049 this.list = Roo.get(document.body).createChild({
44051 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44052 style: 'display:none'
44055 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44058 collapseIf : function(e)
44060 var in_combo = e.within(this.el);
44061 var in_list = e.within(this.list);
44062 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44064 if (in_combo || in_list || is_list) {
44070 onSelect : function(record, index)
44072 if(this.fireEvent('beforeselect', this, record, index) !== false){
44074 this.setFlagClass(record.data.iso2);
44075 this.setDialCode(record.data.dialCode);
44076 this.hasFocus = false;
44078 this.fireEvent('select', this, record, index);
44082 flagEl : function()
44084 var flag = this.el.select('div.flag',true).first();
44091 dialCodeHolderEl : function()
44093 var d = this.el.select('input.dial-code-holder',true).first();
44100 setDialCode : function(v)
44102 this.dialCodeHolder.dom.value = '+'+v;
44105 setFlagClass : function(n)
44107 this.flag.dom.className = 'flag '+n;
44110 getValue : function()
44112 var v = this.inputEl().getValue();
44113 if(this.dialCodeHolder) {
44114 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44119 setValue : function(v)
44121 var d = this.getDialCode(v);
44123 //invalid dial code
44124 if(v.length == 0 || !d || d.length == 0) {
44126 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44127 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44133 this.setFlagClass(this.dialCodeMapping[d].iso2);
44134 this.setDialCode(d);
44135 this.inputEl().dom.value = v.replace('+'+d,'');
44136 this.hiddenEl().dom.value = this.getValue();
44141 getDialCode : function(v)
44145 if (v.length == 0) {
44146 return this.dialCodeHolder.dom.value;
44150 if (v.charAt(0) != "+") {
44153 var numericChars = "";
44154 for (var i = 1; i < v.length; i++) {
44155 var c = v.charAt(i);
44158 if (this.dialCodeMapping[numericChars]) {
44159 dialCode = v.substr(1, i);
44161 if (numericChars.length == 4) {
44171 this.setValue(this.defaultDialCode);
44175 hiddenEl : function()
44177 return this.el.select('input.hidden-tel-input',true).first();
44180 // after setting val
44181 onKeyUp : function(e){
44182 this.setValue(this.getValue());
44185 onKeyPress : function(e){
44186 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44193 * @class Roo.bootstrap.MoneyField
44194 * @extends Roo.bootstrap.ComboBox
44195 * Bootstrap MoneyField class
44198 * Create a new MoneyField.
44199 * @param {Object} config Configuration options
44202 Roo.bootstrap.MoneyField = function(config) {
44204 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44208 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44211 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44213 allowDecimals : true,
44215 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44217 decimalSeparator : ".",
44219 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44221 decimalPrecision : 0,
44223 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44225 allowNegative : true,
44227 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44231 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44233 minValue : Number.NEGATIVE_INFINITY,
44235 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44237 maxValue : Number.MAX_VALUE,
44239 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44241 minText : "The minimum value for this field is {0}",
44243 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44245 maxText : "The maximum value for this field is {0}",
44247 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
44248 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44250 nanText : "{0} is not a valid number",
44252 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44256 * @cfg {String} defaults currency of the MoneyField
44257 * value should be in lkey
44259 defaultCurrency : false,
44261 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44263 thousandsDelimiter : false,
44265 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44276 getAutoCreate : function()
44278 var align = this.labelAlign || this.parentLabelAlign();
44290 cls : 'form-control roo-money-amount-input',
44291 autocomplete: 'new-password'
44294 var hiddenInput = {
44298 cls: 'hidden-number-input'
44301 if(this.max_length) {
44302 input.maxlength = this.max_length;
44306 hiddenInput.name = this.name;
44309 if (this.disabled) {
44310 input.disabled = true;
44313 var clg = 12 - this.inputlg;
44314 var cmd = 12 - this.inputmd;
44315 var csm = 12 - this.inputsm;
44316 var cxs = 12 - this.inputxs;
44320 cls : 'row roo-money-field',
44324 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44328 cls: 'roo-select2-container input-group',
44332 cls : 'form-control roo-money-currency-input',
44333 autocomplete: 'new-password',
44335 name : this.currencyName
44339 cls : 'input-group-addon',
44353 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44357 cls: this.hasFeedback ? 'has-feedback' : '',
44368 if (this.fieldLabel.length) {
44371 tooltip: 'This field is required'
44377 cls: 'control-label',
44383 html: this.fieldLabel
44386 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44392 if(this.indicatorpos == 'right') {
44393 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44400 if(align == 'left') {
44408 if(this.labelWidth > 12){
44409 label.style = "width: " + this.labelWidth + 'px';
44411 if(this.labelWidth < 13 && this.labelmd == 0){
44412 this.labelmd = this.labelWidth;
44414 if(this.labellg > 0){
44415 label.cls += ' col-lg-' + this.labellg;
44416 input.cls += ' col-lg-' + (12 - this.labellg);
44418 if(this.labelmd > 0){
44419 label.cls += ' col-md-' + this.labelmd;
44420 container.cls += ' col-md-' + (12 - this.labelmd);
44422 if(this.labelsm > 0){
44423 label.cls += ' col-sm-' + this.labelsm;
44424 container.cls += ' col-sm-' + (12 - this.labelsm);
44426 if(this.labelxs > 0){
44427 label.cls += ' col-xs-' + this.labelxs;
44428 container.cls += ' col-xs-' + (12 - this.labelxs);
44439 var settings = this;
44441 ['xs','sm','md','lg'].map(function(size){
44442 if (settings[size]) {
44443 cfg.cls += ' col-' + size + '-' + settings[size];
44450 initEvents : function()
44452 this.indicator = this.indicatorEl();
44454 this.initCurrencyEvent();
44456 this.initNumberEvent();
44459 initCurrencyEvent : function()
44462 throw "can not find store for combo";
44465 this.store = Roo.factory(this.store, Roo.data);
44466 this.store.parent = this;
44470 this.triggerEl = this.el.select('.input-group-addon', true).first();
44472 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44477 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44478 _this.list.setWidth(lw);
44481 this.list.on('mouseover', this.onViewOver, this);
44482 this.list.on('mousemove', this.onViewMove, this);
44483 this.list.on('scroll', this.onViewScroll, this);
44486 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44489 this.view = new Roo.View(this.list, this.tpl, {
44490 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44493 this.view.on('click', this.onViewClick, this);
44495 this.store.on('beforeload', this.onBeforeLoad, this);
44496 this.store.on('load', this.onLoad, this);
44497 this.store.on('loadexception', this.onLoadException, this);
44499 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44500 "up" : function(e){
44501 this.inKeyMode = true;
44505 "down" : function(e){
44506 if(!this.isExpanded()){
44507 this.onTriggerClick();
44509 this.inKeyMode = true;
44514 "enter" : function(e){
44517 if(this.fireEvent("specialkey", this, e)){
44518 this.onViewClick(false);
44524 "esc" : function(e){
44528 "tab" : function(e){
44531 if(this.fireEvent("specialkey", this, e)){
44532 this.onViewClick(false);
44540 doRelay : function(foo, bar, hname){
44541 if(hname == 'down' || this.scope.isExpanded()){
44542 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44550 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44554 initNumberEvent : function(e)
44556 this.inputEl().on("keydown" , this.fireKey, this);
44557 this.inputEl().on("focus", this.onFocus, this);
44558 this.inputEl().on("blur", this.onBlur, this);
44560 this.inputEl().relayEvent('keyup', this);
44562 if(this.indicator){
44563 this.indicator.addClass('invisible');
44566 this.originalValue = this.getValue();
44568 if(this.validationEvent == 'keyup'){
44569 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44570 this.inputEl().on('keyup', this.filterValidation, this);
44572 else if(this.validationEvent !== false){
44573 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44576 if(this.selectOnFocus){
44577 this.on("focus", this.preFocus, this);
44580 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44581 this.inputEl().on("keypress", this.filterKeys, this);
44583 this.inputEl().relayEvent('keypress', this);
44586 var allowed = "0123456789";
44588 if(this.allowDecimals){
44589 allowed += this.decimalSeparator;
44592 if(this.allowNegative){
44596 if(this.thousandsDelimiter) {
44600 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44602 var keyPress = function(e){
44604 var k = e.getKey();
44606 var c = e.getCharCode();
44609 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44610 allowed.indexOf(String.fromCharCode(c)) === -1
44616 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44620 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44625 this.inputEl().on("keypress", keyPress, this);
44629 onTriggerClick : function(e)
44636 this.loadNext = false;
44638 if(this.isExpanded()){
44643 this.hasFocus = true;
44645 if(this.triggerAction == 'all') {
44646 this.doQuery(this.allQuery, true);
44650 this.doQuery(this.getRawValue());
44653 getCurrency : function()
44655 var v = this.currencyEl().getValue();
44660 restrictHeight : function()
44662 this.list.alignTo(this.currencyEl(), this.listAlign);
44663 this.list.alignTo(this.currencyEl(), this.listAlign);
44666 onViewClick : function(view, doFocus, el, e)
44668 var index = this.view.getSelectedIndexes()[0];
44670 var r = this.store.getAt(index);
44673 this.onSelect(r, index);
44677 onSelect : function(record, index){
44679 if(this.fireEvent('beforeselect', this, record, index) !== false){
44681 this.setFromCurrencyData(index > -1 ? record.data : false);
44685 this.fireEvent('select', this, record, index);
44689 setFromCurrencyData : function(o)
44693 this.lastCurrency = o;
44695 if (this.currencyField) {
44696 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44698 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44701 this.lastSelectionText = currency;
44703 //setting default currency
44704 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44705 this.setCurrency(this.defaultCurrency);
44709 this.setCurrency(currency);
44712 setFromData : function(o)
44716 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44718 this.setFromCurrencyData(c);
44723 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44725 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44728 this.setValue(value);
44732 setCurrency : function(v)
44734 this.currencyValue = v;
44737 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44742 setValue : function(v)
44744 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44750 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44752 this.inputEl().dom.value = (v == '') ? '' :
44753 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44755 if(!this.allowZero && v === '0') {
44756 this.hiddenEl().dom.value = '';
44757 this.inputEl().dom.value = '';
44764 getRawValue : function()
44766 var v = this.inputEl().getValue();
44771 getValue : function()
44773 return this.fixPrecision(this.parseValue(this.getRawValue()));
44776 parseValue : function(value)
44778 if(this.thousandsDelimiter) {
44780 r = new RegExp(",", "g");
44781 value = value.replace(r, "");
44784 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44785 return isNaN(value) ? '' : value;
44789 fixPrecision : function(value)
44791 if(this.thousandsDelimiter) {
44793 r = new RegExp(",", "g");
44794 value = value.replace(r, "");
44797 var nan = isNaN(value);
44799 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44800 return nan ? '' : value;
44802 return parseFloat(value).toFixed(this.decimalPrecision);
44805 decimalPrecisionFcn : function(v)
44807 return Math.floor(v);
44810 validateValue : function(value)
44812 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44816 var num = this.parseValue(value);
44819 this.markInvalid(String.format(this.nanText, value));
44823 if(num < this.minValue){
44824 this.markInvalid(String.format(this.minText, this.minValue));
44828 if(num > this.maxValue){
44829 this.markInvalid(String.format(this.maxText, this.maxValue));
44836 validate : function()
44838 if(this.disabled || this.allowBlank){
44843 var currency = this.getCurrency();
44845 if(this.validateValue(this.getRawValue()) && currency.length){
44850 this.markInvalid();
44854 getName: function()
44859 beforeBlur : function()
44865 var v = this.parseValue(this.getRawValue());
44872 onBlur : function()
44876 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44877 //this.el.removeClass(this.focusClass);
44880 this.hasFocus = false;
44882 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44886 var v = this.getValue();
44888 if(String(v) !== String(this.startValue)){
44889 this.fireEvent('change', this, v, this.startValue);
44892 this.fireEvent("blur", this);
44895 inputEl : function()
44897 return this.el.select('.roo-money-amount-input', true).first();
44900 currencyEl : function()
44902 return this.el.select('.roo-money-currency-input', true).first();
44905 hiddenEl : function()
44907 return this.el.select('input.hidden-number-input',true).first();
44911 * @class Roo.bootstrap.BezierSignature
44912 * @extends Roo.bootstrap.Component
44913 * Bootstrap BezierSignature class
44914 * This script refer to:
44915 * Title: Signature Pad
44917 * Availability: https://github.com/szimek/signature_pad
44920 * Create a new BezierSignature
44921 * @param {Object} config The config object
44924 Roo.bootstrap.BezierSignature = function(config){
44925 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44931 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44938 mouse_btn_down: true,
44941 * @cfg {int} canvas height
44943 canvas_height: '200px',
44946 * @cfg {float|function} Radius of a single dot.
44951 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44956 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44961 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44966 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44971 * @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.
44973 bg_color: 'rgba(0, 0, 0, 0)',
44976 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44978 dot_color: 'black',
44981 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44983 velocity_filter_weight: 0.7,
44986 * @cfg {function} Callback when stroke begin.
44991 * @cfg {function} Callback when stroke end.
44995 getAutoCreate : function()
44997 var cls = 'roo-signature column';
45000 cls += ' ' + this.cls;
45010 for(var i = 0; i < col_sizes.length; i++) {
45011 if(this[col_sizes[i]]) {
45012 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
45022 cls: 'roo-signature-body',
45026 cls: 'roo-signature-body-canvas',
45027 height: this.canvas_height,
45028 width: this.canvas_width
45035 style: 'display: none'
45043 initEvents: function()
45045 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45047 var canvas = this.canvasEl();
45049 // mouse && touch event swapping...
45050 canvas.dom.style.touchAction = 'none';
45051 canvas.dom.style.msTouchAction = 'none';
45053 this.mouse_btn_down = false;
45054 canvas.on('mousedown', this._handleMouseDown, this);
45055 canvas.on('mousemove', this._handleMouseMove, this);
45056 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45058 if (window.PointerEvent) {
45059 canvas.on('pointerdown', this._handleMouseDown, this);
45060 canvas.on('pointermove', this._handleMouseMove, this);
45061 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45064 if ('ontouchstart' in window) {
45065 canvas.on('touchstart', this._handleTouchStart, this);
45066 canvas.on('touchmove', this._handleTouchMove, this);
45067 canvas.on('touchend', this._handleTouchEnd, this);
45070 Roo.EventManager.onWindowResize(this.resize, this, true);
45072 // file input event
45073 this.fileEl().on('change', this.uploadImage, this);
45080 resize: function(){
45082 var canvas = this.canvasEl().dom;
45083 var ctx = this.canvasElCtx();
45084 var img_data = false;
45086 if(canvas.width > 0) {
45087 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45089 // setting canvas width will clean img data
45092 var style = window.getComputedStyle ?
45093 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45095 var padding_left = parseInt(style.paddingLeft) || 0;
45096 var padding_right = parseInt(style.paddingRight) || 0;
45098 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45101 ctx.putImageData(img_data, 0, 0);
45105 _handleMouseDown: function(e)
45107 if (e.browserEvent.which === 1) {
45108 this.mouse_btn_down = true;
45109 this.strokeBegin(e);
45113 _handleMouseMove: function (e)
45115 if (this.mouse_btn_down) {
45116 this.strokeMoveUpdate(e);
45120 _handleMouseUp: function (e)
45122 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45123 this.mouse_btn_down = false;
45128 _handleTouchStart: function (e) {
45130 e.preventDefault();
45131 if (e.browserEvent.targetTouches.length === 1) {
45132 // var touch = e.browserEvent.changedTouches[0];
45133 // this.strokeBegin(touch);
45135 this.strokeBegin(e); // assume e catching the correct xy...
45139 _handleTouchMove: function (e) {
45140 e.preventDefault();
45141 // var touch = event.targetTouches[0];
45142 // _this._strokeMoveUpdate(touch);
45143 this.strokeMoveUpdate(e);
45146 _handleTouchEnd: function (e) {
45147 var wasCanvasTouched = e.target === this.canvasEl().dom;
45148 if (wasCanvasTouched) {
45149 e.preventDefault();
45150 // var touch = event.changedTouches[0];
45151 // _this._strokeEnd(touch);
45156 reset: function () {
45157 this._lastPoints = [];
45158 this._lastVelocity = 0;
45159 this._lastWidth = (this.min_width + this.max_width) / 2;
45160 this.canvasElCtx().fillStyle = this.dot_color;
45163 strokeMoveUpdate: function(e)
45165 this.strokeUpdate(e);
45167 if (this.throttle) {
45168 this.throttleStroke(this.strokeUpdate, this.throttle);
45171 this.strokeUpdate(e);
45175 strokeBegin: function(e)
45177 var newPointGroup = {
45178 color: this.dot_color,
45182 if (typeof this.onBegin === 'function') {
45186 this.curve_data.push(newPointGroup);
45188 this.strokeUpdate(e);
45191 strokeUpdate: function(e)
45193 var rect = this.canvasEl().dom.getBoundingClientRect();
45194 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45195 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45196 var lastPoints = lastPointGroup.points;
45197 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45198 var isLastPointTooClose = lastPoint
45199 ? point.distanceTo(lastPoint) <= this.min_distance
45201 var color = lastPointGroup.color;
45202 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45203 var curve = this.addPoint(point);
45205 this.drawDot({color: color, point: point});
45208 this.drawCurve({color: color, curve: curve});
45218 strokeEnd: function(e)
45220 this.strokeUpdate(e);
45221 if (typeof this.onEnd === 'function') {
45226 addPoint: function (point) {
45227 var _lastPoints = this._lastPoints;
45228 _lastPoints.push(point);
45229 if (_lastPoints.length > 2) {
45230 if (_lastPoints.length === 3) {
45231 _lastPoints.unshift(_lastPoints[0]);
45233 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45234 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45235 _lastPoints.shift();
45241 calculateCurveWidths: function (startPoint, endPoint) {
45242 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45243 (1 - this.velocity_filter_weight) * this._lastVelocity;
45245 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45248 start: this._lastWidth
45251 this._lastVelocity = velocity;
45252 this._lastWidth = newWidth;
45256 drawDot: function (_a) {
45257 var color = _a.color, point = _a.point;
45258 var ctx = this.canvasElCtx();
45259 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45261 this.drawCurveSegment(point.x, point.y, width);
45263 ctx.fillStyle = color;
45267 drawCurve: function (_a) {
45268 var color = _a.color, curve = _a.curve;
45269 var ctx = this.canvasElCtx();
45270 var widthDelta = curve.endWidth - curve.startWidth;
45271 var drawSteps = Math.floor(curve.length()) * 2;
45273 ctx.fillStyle = color;
45274 for (var i = 0; i < drawSteps; i += 1) {
45275 var t = i / drawSteps;
45281 var x = uuu * curve.startPoint.x;
45282 x += 3 * uu * t * curve.control1.x;
45283 x += 3 * u * tt * curve.control2.x;
45284 x += ttt * curve.endPoint.x;
45285 var y = uuu * curve.startPoint.y;
45286 y += 3 * uu * t * curve.control1.y;
45287 y += 3 * u * tt * curve.control2.y;
45288 y += ttt * curve.endPoint.y;
45289 var width = curve.startWidth + ttt * widthDelta;
45290 this.drawCurveSegment(x, y, width);
45296 drawCurveSegment: function (x, y, width) {
45297 var ctx = this.canvasElCtx();
45299 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45300 this.is_empty = false;
45305 var ctx = this.canvasElCtx();
45306 var canvas = this.canvasEl().dom;
45307 ctx.fillStyle = this.bg_color;
45308 ctx.clearRect(0, 0, canvas.width, canvas.height);
45309 ctx.fillRect(0, 0, canvas.width, canvas.height);
45310 this.curve_data = [];
45312 this.is_empty = true;
45317 return this.el.select('input',true).first();
45320 canvasEl: function()
45322 return this.el.select('canvas',true).first();
45325 canvasElCtx: function()
45327 return this.el.select('canvas',true).first().dom.getContext('2d');
45330 getImage: function(type)
45332 if(this.is_empty) {
45337 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45340 drawFromImage: function(img_src)
45342 var img = new Image();
45344 img.onload = function(){
45345 this.canvasElCtx().drawImage(img, 0, 0);
45350 this.is_empty = false;
45353 selectImage: function()
45355 this.fileEl().dom.click();
45358 uploadImage: function(e)
45360 var reader = new FileReader();
45362 reader.onload = function(e){
45363 var img = new Image();
45364 img.onload = function(){
45366 this.canvasElCtx().drawImage(img, 0, 0);
45368 img.src = e.target.result;
45371 reader.readAsDataURL(e.target.files[0]);
45374 // Bezier Point Constructor
45375 Point: (function () {
45376 function Point(x, y, time) {
45379 this.time = time || Date.now();
45381 Point.prototype.distanceTo = function (start) {
45382 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45384 Point.prototype.equals = function (other) {
45385 return this.x === other.x && this.y === other.y && this.time === other.time;
45387 Point.prototype.velocityFrom = function (start) {
45388 return this.time !== start.time
45389 ? this.distanceTo(start) / (this.time - start.time)
45396 // Bezier Constructor
45397 Bezier: (function () {
45398 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45399 this.startPoint = startPoint;
45400 this.control2 = control2;
45401 this.control1 = control1;
45402 this.endPoint = endPoint;
45403 this.startWidth = startWidth;
45404 this.endWidth = endWidth;
45406 Bezier.fromPoints = function (points, widths, scope) {
45407 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45408 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45409 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45411 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45412 var dx1 = s1.x - s2.x;
45413 var dy1 = s1.y - s2.y;
45414 var dx2 = s2.x - s3.x;
45415 var dy2 = s2.y - s3.y;
45416 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45417 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45418 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45419 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45420 var dxm = m1.x - m2.x;
45421 var dym = m1.y - m2.y;
45422 var k = l2 / (l1 + l2);
45423 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45424 var tx = s2.x - cm.x;
45425 var ty = s2.y - cm.y;
45427 c1: new scope.Point(m1.x + tx, m1.y + ty),
45428 c2: new scope.Point(m2.x + tx, m2.y + ty)
45431 Bezier.prototype.length = function () {
45436 for (var i = 0; i <= steps; i += 1) {
45438 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45439 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45441 var xdiff = cx - px;
45442 var ydiff = cy - py;
45443 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45450 Bezier.prototype.point = function (t, start, c1, c2, end) {
45451 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45452 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45453 + (3.0 * c2 * (1.0 - t) * t * t)
45454 + (end * t * t * t);
45459 throttleStroke: function(fn, wait) {
45460 if (wait === void 0) { wait = 250; }
45462 var timeout = null;
45466 var later = function () {
45467 previous = Date.now();
45469 result = fn.apply(storedContext, storedArgs);
45471 storedContext = null;
45475 return function wrapper() {
45477 for (var _i = 0; _i < arguments.length; _i++) {
45478 args[_i] = arguments[_i];
45480 var now = Date.now();
45481 var remaining = wait - (now - previous);
45482 storedContext = this;
45484 if (remaining <= 0 || remaining > wait) {
45486 clearTimeout(timeout);
45490 result = fn.apply(storedContext, storedArgs);
45492 storedContext = null;
45496 else if (!timeout) {
45497 timeout = window.setTimeout(later, remaining);