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 * @children Roo.bootstrap.Component
656 * Bootstrap Element class (basically a DIV used to make random stuff )
658 * @cfg {String} html contents of the element
659 * @cfg {String} tag tag of the element
660 * @cfg {String} cls class of the element
661 * @cfg {Boolean} preventDefault (true|false) default false
662 * @cfg {Boolean} clickable (true|false) default false
663 * @cfg {String} role default blank - set to button to force cursor pointer
667 * Create a new Element
668 * @param {Object} config The config object
671 Roo.bootstrap.Element = function(config){
672 Roo.bootstrap.Element.superclass.constructor.call(this, config);
678 * When a element is chick
679 * @param {Roo.bootstrap.Element} this
680 * @param {Roo.EventObject} e
688 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
693 preventDefault: false,
698 getAutoCreate : function(){
702 // cls: this.cls, double assign in parent class Component.js :: onRender
705 if (this.role !== false) {
706 cfg.role = this.role;
712 initEvents: function()
714 Roo.bootstrap.Element.superclass.initEvents.call(this);
717 this.el.on('click', this.onClick, this);
723 onClick : function(e)
725 if(this.preventDefault){
729 this.fireEvent('click', this, e); // why was this double click before?
737 getValue : function()
739 return this.el.dom.innerHTML;
742 setValue : function(value)
744 this.el.dom.innerHTML = value;
759 * @class Roo.bootstrap.DropTarget
760 * @extends Roo.bootstrap.Element
761 * Bootstrap DropTarget class
763 * @cfg {string} name dropable name
766 * Create a new Dropable Area
767 * @param {Object} config The config object
770 Roo.bootstrap.DropTarget = function(config){
771 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
777 * When a element is chick
778 * @param {Roo.bootstrap.Element} this
779 * @param {Roo.EventObject} e
785 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
788 getAutoCreate : function(){
793 initEvents: function()
795 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
796 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
799 drop : this.dragDrop.createDelegate(this),
800 enter : this.dragEnter.createDelegate(this),
801 out : this.dragOut.createDelegate(this),
802 over : this.dragOver.createDelegate(this)
806 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
809 dragDrop : function(source,e,data)
811 // user has to decide how to impliment this.
814 //this.fireEvent('drop', this, source, e ,data);
818 dragEnter : function(n, dd, e, data)
820 // probably want to resize the element to match the dropped element..
822 this.originalSize = this.el.getSize();
823 this.el.setSize( n.el.getSize());
824 this.dropZone.DDM.refreshCache(this.name);
825 Roo.log([n, dd, e, data]);
828 dragOut : function(value)
830 // resize back to normal
832 this.el.setSize(this.originalSize);
833 this.dropZone.resetConstraints();
836 dragOver : function()
853 * @class Roo.bootstrap.Body
854 * @extends Roo.bootstrap.Component
856 * @children Roo.bootstrap.Component
858 * Bootstrap Body class
862 * @param {Object} config The config object
865 Roo.bootstrap.Body = function(config){
867 config = config || {};
869 Roo.bootstrap.Body.superclass.constructor.call(this, config);
870 this.el = Roo.get(config.el ? config.el : document.body );
871 if (this.cls && this.cls.length) {
872 Roo.get(document.body).addClass(this.cls);
876 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
878 is_body : true,// just to make sure it's constructed?
883 onRender : function(ct, position)
885 /* Roo.log("Roo.bootstrap.Body - onRender");
886 if (this.cls && this.cls.length) {
887 Roo.get(document.body).addClass(this.cls);
906 * @class Roo.bootstrap.ButtonGroup
907 * @extends Roo.bootstrap.Component
908 * Bootstrap ButtonGroup class
909 * @children Roo.bootstrap.Button Roo.bootstrap.Form
911 * @cfg {String} size lg | sm | xs (default empty normal)
912 * @cfg {String} align vertical | justified (default none)
913 * @cfg {String} direction up | down (default down)
914 * @cfg {Boolean} toolbar false | true
915 * @cfg {Boolean} btn true | false
920 * @param {Object} config The config object
923 Roo.bootstrap.ButtonGroup = function(config){
924 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
927 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
935 getAutoCreate : function(){
941 cfg.html = this.html || cfg.html;
952 if (['vertical','justified'].indexOf(this.align)!==-1) {
953 cfg.cls = 'btn-group-' + this.align;
955 if (this.align == 'justified') {
956 console.log(this.items);
960 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
961 cfg.cls += ' btn-group-' + this.size;
964 if (this.direction == 'up') {
965 cfg.cls += ' dropup' ;
971 * Add a button to the group (similar to NavItem API.)
973 addItem : function(cfg)
975 var cn = new Roo.bootstrap.Button(cfg);
977 cn.parentId = this.id;
978 cn.onRender(this.el, null);
992 * @class Roo.bootstrap.Button
993 * @extends Roo.bootstrap.Component
994 * Bootstrap Button class
995 * @cfg {String} html The button content
996 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
997 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
998 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
999 * @cfg {String} size (lg|sm|xs)
1000 * @cfg {String} tag (a|input|submit)
1001 * @cfg {String} href empty or href
1002 * @cfg {Boolean} disabled default false;
1003 * @cfg {Boolean} isClose default false;
1004 * @cfg {String} glyphicon depricated - use fa
1005 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1006 * @cfg {String} badge text for badge
1007 * @cfg {String} theme (default|glow)
1008 * @cfg {Boolean} inverse dark themed version
1009 * @cfg {Boolean} toggle is it a slidy toggle button
1010 * @cfg {Boolean} pressed default null - if the button ahs active state
1011 * @cfg {String} ontext text for on slidy toggle state
1012 * @cfg {String} offtext text for off slidy toggle state
1013 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1014 * @cfg {Boolean} removeClass remove the standard class..
1015 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1016 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1017 * @cfg {Roo.bootstrap.Menu} menu a Menu
1020 * Create a new button
1021 * @param {Object} config The config object
1025 Roo.bootstrap.Button = function(config){
1026 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1032 * When a button is pressed
1033 * @param {Roo.bootstrap.Button} btn
1034 * @param {Roo.EventObject} e
1039 * When a button is double clicked
1040 * @param {Roo.bootstrap.Button} btn
1041 * @param {Roo.EventObject} e
1046 * After the button has been toggles
1047 * @param {Roo.bootstrap.Button} btn
1048 * @param {Roo.EventObject} e
1049 * @param {boolean} pressed (also available as button.pressed)
1055 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1076 preventDefault: true,
1085 getAutoCreate : function(){
1093 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1094 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1095 this.tag = 'button';
1099 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1101 if (this.toggle == true) {
1104 cls: 'slider-frame roo-button',
1108 'data-on-text':'ON',
1109 'data-off-text':'OFF',
1110 cls: 'slider-button',
1115 // why are we validating the weights?
1116 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1117 cfg.cls += ' ' + this.weight;
1124 cfg.cls += ' close';
1126 cfg["aria-hidden"] = true;
1128 cfg.html = "×";
1134 if (this.theme==='default') {
1135 cfg.cls = 'btn roo-button';
1137 //if (this.parentType != 'Navbar') {
1138 this.weight = this.weight.length ? this.weight : 'default';
1140 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1142 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1143 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1144 cfg.cls += ' btn-' + outline + weight;
1145 if (this.weight == 'default') {
1147 cfg.cls += ' btn-' + this.weight;
1150 } else if (this.theme==='glow') {
1153 cfg.cls = 'btn-glow roo-button';
1155 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1157 cfg.cls += ' ' + this.weight;
1163 this.cls += ' inverse';
1167 if (this.active || this.pressed === true) {
1168 cfg.cls += ' active';
1171 if (this.disabled) {
1172 cfg.disabled = 'disabled';
1176 Roo.log('changing to ul' );
1178 this.glyphicon = 'caret';
1179 if (Roo.bootstrap.version == 4) {
1180 this.fa = 'caret-down';
1185 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1187 //gsRoo.log(this.parentType);
1188 if (this.parentType === 'Navbar' && !this.parent().bar) {
1189 Roo.log('changing to li?');
1198 href : this.href || '#'
1201 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1202 cfg.cls += ' dropdown';
1209 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1211 if (this.glyphicon) {
1212 cfg.html = ' ' + cfg.html;
1217 cls: 'glyphicon glyphicon-' + this.glyphicon
1222 cfg.html = ' ' + cfg.html;
1227 cls: 'fa fas fa-' + this.fa
1237 // cfg.cls='btn roo-button';
1241 var value = cfg.html;
1246 cls: 'glyphicon glyphicon-' + this.glyphicon,
1253 cls: 'fa fas fa-' + this.fa,
1258 var bw = this.badge_weight.length ? this.badge_weight :
1259 (this.weight.length ? this.weight : 'secondary');
1260 bw = bw == 'default' ? 'secondary' : bw;
1266 cls: 'badge badge-' + bw,
1275 cfg.cls += ' dropdown';
1276 cfg.html = typeof(cfg.html) != 'undefined' ?
1277 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1280 if (cfg.tag !== 'a' && this.href !== '') {
1281 throw "Tag must be a to set href.";
1282 } else if (this.href.length > 0) {
1283 cfg.href = this.href;
1286 if(this.removeClass){
1291 cfg.target = this.target;
1296 initEvents: function() {
1297 // Roo.log('init events?');
1298 // Roo.log(this.el.dom);
1301 if (typeof (this.menu) != 'undefined') {
1302 this.menu.parentType = this.xtype;
1303 this.menu.triggerEl = this.el;
1304 this.addxtype(Roo.apply({}, this.menu));
1308 if (this.el.hasClass('roo-button')) {
1309 this.el.on('click', this.onClick, this);
1310 this.el.on('dblclick', this.onDblClick, this);
1312 this.el.select('.roo-button').on('click', this.onClick, this);
1313 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1317 if(this.removeClass){
1318 this.el.on('click', this.onClick, this);
1321 if (this.group === true) {
1322 if (this.pressed === false || this.pressed === true) {
1325 this.pressed = false;
1326 this.setActive(this.pressed);
1331 this.el.enableDisplayMode();
1334 onClick : function(e)
1336 if (this.disabled) {
1340 Roo.log('button on click ');
1341 if(this.preventDefault){
1350 this.setActive(true);
1351 var pi = this.parent().items;
1352 for (var i = 0;i < pi.length;i++) {
1353 if (this == pi[i]) {
1356 if (pi[i].el.hasClass('roo-button')) {
1357 pi[i].setActive(false);
1360 this.fireEvent('click', this, e);
1364 if (this.pressed === true || this.pressed === false) {
1365 this.toggleActive(e);
1369 this.fireEvent('click', this, e);
1371 onDblClick: function(e)
1373 if (this.disabled) {
1376 if(this.preventDefault){
1379 this.fireEvent('dblclick', this, e);
1382 * Enables this button
1386 this.disabled = false;
1387 this.el.removeClass('disabled');
1388 this.el.dom.removeAttribute("disabled");
1392 * Disable this button
1394 disable : function()
1396 this.disabled = true;
1397 this.el.addClass('disabled');
1398 this.el.attr("disabled", "disabled")
1401 * sets the active state on/off,
1402 * @param {Boolean} state (optional) Force a particular state
1404 setActive : function(v) {
1406 this.el[v ? 'addClass' : 'removeClass']('active');
1410 * toggles the current active state
1412 toggleActive : function(e)
1414 this.setActive(!this.pressed); // this modifies pressed...
1415 this.fireEvent('toggle', this, e, this.pressed);
1418 * get the current active state
1419 * @return {boolean} true if it's active
1421 isActive : function()
1423 return this.el.hasClass('active');
1426 * set the text of the first selected button
1428 setText : function(str)
1430 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1433 * get the text of the first selected button
1435 getText : function()
1437 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1440 setWeight : function(str)
1442 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1443 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1445 var outline = this.outline ? 'outline-' : '';
1446 if (str == 'default') {
1447 this.el.addClass('btn-default btn-outline-secondary');
1450 this.el.addClass('btn-' + outline + str);
1455 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1457 Roo.bootstrap.Button.weights = [
1477 * @class Roo.bootstrap.Column
1478 * @extends Roo.bootstrap.Component
1479 * @children Roo.bootstrap.Component
1480 * Bootstrap Column class
1481 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1482 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1483 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1484 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1485 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1486 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1487 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1488 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1491 * @cfg {Boolean} hidden (true|false) hide the element
1492 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1493 * @cfg {String} fa (ban|check|...) font awesome icon
1494 * @cfg {Number} fasize (1|2|....) font awsome size
1496 * @cfg {String} icon (info-sign|check|...) glyphicon name
1498 * @cfg {String} html content of column.
1501 * Create a new Column
1502 * @param {Object} config The config object
1505 Roo.bootstrap.Column = function(config){
1506 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1509 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1527 getAutoCreate : function(){
1528 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1536 var sizes = ['xs','sm','md','lg'];
1537 sizes.map(function(size ,ix){
1538 //Roo.log( size + ':' + settings[size]);
1540 if (settings[size+'off'] !== false) {
1541 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1544 if (settings[size] === false) {
1548 if (!settings[size]) { // 0 = hidden
1549 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1551 for (var i = ix; i > -1; i--) {
1552 cfg.cls += ' d-' + sizes[i] + '-none';
1558 cfg.cls += ' col-' + size + '-' + settings[size] + (
1559 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1565 cfg.cls += ' hidden';
1568 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1569 cfg.cls +=' alert alert-' + this.alert;
1573 if (this.html.length) {
1574 cfg.html = this.html;
1578 if (this.fasize > 1) {
1579 fasize = ' fa-' + this.fasize + 'x';
1581 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1586 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1605 * @class Roo.bootstrap.Container
1606 * @extends Roo.bootstrap.Component
1608 * @children Roo.bootstrap.Component
1609 * Bootstrap Container class
1610 * @cfg {Boolean} jumbotron is it a jumbotron element
1611 * @cfg {String} html content of element
1612 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1613 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1614 * @cfg {String} header content of header (for panel)
1615 * @cfg {String} footer content of footer (for panel)
1616 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1617 * @cfg {String} tag (header|aside|section) type of HTML tag.
1618 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1619 * @cfg {String} fa font awesome icon
1620 * @cfg {String} icon (info-sign|check|...) glyphicon name
1621 * @cfg {Boolean} hidden (true|false) hide the element
1622 * @cfg {Boolean} expandable (true|false) default false
1623 * @cfg {Boolean} expanded (true|false) default true
1624 * @cfg {String} rheader contet on the right of header
1625 * @cfg {Boolean} clickable (true|false) default false
1629 * Create a new Container
1630 * @param {Object} config The config object
1633 Roo.bootstrap.Container = function(config){
1634 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1640 * After the panel has been expand
1642 * @param {Roo.bootstrap.Container} this
1647 * After the panel has been collapsed
1649 * @param {Roo.bootstrap.Container} this
1654 * When a element is chick
1655 * @param {Roo.bootstrap.Container} this
1656 * @param {Roo.EventObject} e
1662 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1680 getChildContainer : function() {
1686 if (this.panel.length) {
1687 return this.el.select('.panel-body',true).first();
1694 getAutoCreate : function(){
1697 tag : this.tag || 'div',
1701 if (this.jumbotron) {
1702 cfg.cls = 'jumbotron';
1707 // - this is applied by the parent..
1709 // cfg.cls = this.cls + '';
1712 if (this.sticky.length) {
1714 var bd = Roo.get(document.body);
1715 if (!bd.hasClass('bootstrap-sticky')) {
1716 bd.addClass('bootstrap-sticky');
1717 Roo.select('html',true).setStyle('height', '100%');
1720 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1724 if (this.well.length) {
1725 switch (this.well) {
1728 cfg.cls +=' well well-' +this.well;
1737 cfg.cls += ' hidden';
1741 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1742 cfg.cls +=' alert alert-' + this.alert;
1747 if (this.panel.length) {
1748 cfg.cls += ' panel panel-' + this.panel;
1750 if (this.header.length) {
1754 if(this.expandable){
1756 cfg.cls = cfg.cls + ' expandable';
1760 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1768 cls : 'panel-title',
1769 html : (this.expandable ? ' ' : '') + this.header
1773 cls: 'panel-header-right',
1779 cls : 'panel-heading',
1780 style : this.expandable ? 'cursor: pointer' : '',
1788 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1793 if (this.footer.length) {
1795 cls : 'panel-footer',
1804 body.html = this.html || cfg.html;
1805 // prefix with the icons..
1807 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1810 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1815 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1816 cfg.cls = 'container';
1822 initEvents: function()
1824 if(this.expandable){
1825 var headerEl = this.headerEl();
1828 headerEl.on('click', this.onToggleClick, this);
1833 this.el.on('click', this.onClick, this);
1838 onToggleClick : function()
1840 var headerEl = this.headerEl();
1856 if(this.fireEvent('expand', this)) {
1858 this.expanded = true;
1860 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1862 this.el.select('.panel-body',true).first().removeClass('hide');
1864 var toggleEl = this.toggleEl();
1870 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1875 collapse : function()
1877 if(this.fireEvent('collapse', this)) {
1879 this.expanded = false;
1881 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1882 this.el.select('.panel-body',true).first().addClass('hide');
1884 var toggleEl = this.toggleEl();
1890 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1894 toggleEl : function()
1896 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1900 return this.el.select('.panel-heading .fa',true).first();
1903 headerEl : function()
1905 if(!this.el || !this.panel.length || !this.header.length){
1909 return this.el.select('.panel-heading',true).first()
1914 if(!this.el || !this.panel.length){
1918 return this.el.select('.panel-body',true).first()
1921 titleEl : function()
1923 if(!this.el || !this.panel.length || !this.header.length){
1927 return this.el.select('.panel-title',true).first();
1930 setTitle : function(v)
1932 var titleEl = this.titleEl();
1938 titleEl.dom.innerHTML = v;
1941 getTitle : function()
1944 var titleEl = this.titleEl();
1950 return titleEl.dom.innerHTML;
1953 setRightTitle : function(v)
1955 var t = this.el.select('.panel-header-right',true).first();
1961 t.dom.innerHTML = v;
1964 onClick : function(e)
1968 this.fireEvent('click', this, e);
1973 * @class Roo.bootstrap.Card
1974 * @extends Roo.bootstrap.Component
1975 * @children Roo.bootstrap.Component
1977 * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
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 * @parent Roo.bootstrap.Card
2779 * @children Roo.bootstrap.Component
2780 * Bootstrap CardHeader class
2782 * Create a new Card Header - that you can embed children into
2783 * @param {Object} config The config object
2786 Roo.bootstrap.CardHeader = function(config){
2787 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2790 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2793 container_method : 'getCardHeader'
2806 * Card footer - holder for the card footer elements.
2811 * @class Roo.bootstrap.CardFooter
2812 * @extends Roo.bootstrap.Element
2813 * @parent Roo.bootstrap.Card
2814 * @children Roo.bootstrap.Component
2815 * Bootstrap CardFooter class
2818 * Create a new Card Footer - that you can embed children into
2819 * @param {Object} config The config object
2822 Roo.bootstrap.CardFooter = function(config){
2823 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2826 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2829 container_method : 'getCardFooter'
2842 * Card header - holder for the card header elements.
2847 * @class Roo.bootstrap.CardImageTop
2848 * @extends Roo.bootstrap.Element
2849 * @parent Roo.bootstrap.Card
2850 * @children Roo.bootstrap.Component
2851 * Bootstrap CardImageTop class
2854 * Create a new Card Image Top container
2855 * @param {Object} config The config object
2858 Roo.bootstrap.CardImageTop = function(config){
2859 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2862 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2865 container_method : 'getCardImageTop'
2880 * @class Roo.bootstrap.ButtonUploader
2881 * @extends Roo.bootstrap.Button
2882 * Bootstrap Button Uploader class - it's a button which when you add files to it
2885 * @cfg {Number} errorTimeout default 3000
2886 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2887 * @cfg {Array} html The button text.
2888 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2891 * Create a new CardUploader
2892 * @param {Object} config The config object
2895 Roo.bootstrap.ButtonUploader = function(config){
2899 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2905 * @event beforeselect
2906 * When button is pressed, before show upload files dialog is shown
2907 * @param {Roo.bootstrap.UploaderButton} this
2910 'beforeselect' : true,
2912 * @event fired when files have been selected,
2913 * When a the download link is clicked
2914 * @param {Roo.bootstrap.UploaderButton} this
2915 * @param {Array} Array of files that have been uploaded
2922 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2925 errorTimeout : 3000,
2929 fileCollection : false,
2934 getAutoCreate : function()
2939 cls : 'd-none roo-card-upload-selector'
2942 if (this.multiple) {
2943 im.multiple = 'multiple';
2949 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2959 initEvents : function()
2962 Roo.bootstrap.Button.prototype.initEvents.call(this);
2968 this.urlAPI = (window.createObjectURL && window) ||
2969 (window.URL && URL.revokeObjectURL && URL) ||
2970 (window.webkitURL && webkitURL);
2975 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2977 this.selectorEl.on('change', this.onFileSelected, this);
2984 onClick : function(e)
2988 if ( this.fireEvent('beforeselect', this) === false) {
2992 this.selectorEl.dom.click();
2996 onFileSelected : function(e)
3000 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3003 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3004 this.selectorEl.dom.value = '';// hopefully reset..
3006 this.fireEvent('uploaded', this, files );
3014 * addCard - add an Attachment to the uploader
3015 * @param data - the data about the image to upload
3019 title : "Title of file",
3020 is_uploaded : false,
3021 src : "http://.....",
3022 srcfile : { the File upload object },
3023 mimetype : file.type,
3026 .. any other data...
3051 * @class Roo.bootstrap.Img
3052 * @extends Roo.bootstrap.Component
3053 * Bootstrap Img class
3054 * @cfg {Boolean} imgResponsive false | true
3055 * @cfg {String} border rounded | circle | thumbnail
3056 * @cfg {String} src image source
3057 * @cfg {String} alt image alternative text
3058 * @cfg {String} href a tag href
3059 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3060 * @cfg {String} xsUrl xs image source
3061 * @cfg {String} smUrl sm image source
3062 * @cfg {String} mdUrl md image source
3063 * @cfg {String} lgUrl lg image source
3064 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3067 * Create a new Input
3068 * @param {Object} config The config object
3071 Roo.bootstrap.Img = function(config){
3072 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3078 * The img click event for the img.
3079 * @param {Roo.EventObject} e
3084 * The when any image loads
3085 * @param {Roo.EventObject} e
3091 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3093 imgResponsive: true,
3102 backgroundContain : false,
3104 getAutoCreate : function()
3106 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3107 return this.createSingleImg();
3112 cls: 'roo-image-responsive-group',
3117 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3119 if(!_this[size + 'Url']){
3125 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3126 html: _this.html || cfg.html,
3127 src: _this[size + 'Url']
3130 img.cls += ' roo-image-responsive-' + size;
3132 var s = ['xs', 'sm', 'md', 'lg'];
3134 s.splice(s.indexOf(size), 1);
3136 Roo.each(s, function(ss){
3137 img.cls += ' hidden-' + ss;
3140 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3141 cfg.cls += ' img-' + _this.border;
3145 cfg.alt = _this.alt;
3158 a.target = _this.target;
3162 cfg.cn.push((_this.href) ? a : img);
3169 createSingleImg : function()
3173 cls: (this.imgResponsive) ? 'img-responsive' : '',
3175 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3178 if (this.backgroundContain) {
3179 cfg.cls += ' background-contain';
3182 cfg.html = this.html || cfg.html;
3184 if (this.backgroundContain) {
3185 cfg.style="background-image: url(" + this.src + ')';
3187 cfg.src = this.src || cfg.src;
3190 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3191 cfg.cls += ' img-' + this.border;
3208 a.target = this.target;
3213 return (this.href) ? a : cfg;
3216 initEvents: function()
3219 this.el.on('click', this.onClick, this);
3221 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3222 this.el.on('load', this.onImageLoad, this);
3224 // not sure if this works.. not tested
3225 this.el.select('img', true).on('load', this.onImageLoad, this);
3230 onClick : function(e)
3232 Roo.log('img onclick');
3233 this.fireEvent('click', this, e);
3235 onImageLoad: function(e)
3237 Roo.log('img load');
3238 this.fireEvent('load', this, e);
3242 * Sets the url of the image - used to update it
3243 * @param {String} url the url of the image
3246 setSrc : function(url)
3250 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3251 if (this.backgroundContain) {
3252 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3254 this.el.dom.src = url;
3259 this.el.select('img', true).first().dom.src = url;
3275 * @class Roo.bootstrap.Link
3276 * @extends Roo.bootstrap.Component
3277 * @children Roo.bootstrap.Component
3278 * Bootstrap Link Class (eg. '<a href>')
3280 * @cfg {String} alt image alternative text
3281 * @cfg {String} href a tag href
3282 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3283 * @cfg {String} html the content of the link.
3284 * @cfg {String} anchor name for the anchor link
3285 * @cfg {String} fa - favicon
3287 * @cfg {Boolean} preventDefault (true | false) default false
3291 * Create a new Input
3292 * @param {Object} config The config object
3295 Roo.bootstrap.Link = function(config){
3296 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3302 * The img click event for the img.
3303 * @param {Roo.EventObject} e
3309 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3313 preventDefault: false,
3319 getAutoCreate : function()
3321 var html = this.html || '';
3323 if (this.fa !== false) {
3324 html = '<i class="fa fa-' + this.fa + '"></i>';
3329 // anchor's do not require html/href...
3330 if (this.anchor === false) {
3332 cfg.href = this.href || '#';
3334 cfg.name = this.anchor;
3335 if (this.html !== false || this.fa !== false) {
3338 if (this.href !== false) {
3339 cfg.href = this.href;
3343 if(this.alt !== false){
3348 if(this.target !== false) {
3349 cfg.target = this.target;
3355 initEvents: function() {
3357 if(!this.href || this.preventDefault){
3358 this.el.on('click', this.onClick, this);
3362 onClick : function(e)
3364 if(this.preventDefault){
3367 //Roo.log('img onclick');
3368 this.fireEvent('click', this, e);
3381 * @class Roo.bootstrap.Header
3382 * @extends Roo.bootstrap.Component
3383 * Bootstrap Header class
3384 * @cfg {String} html content of header
3385 * @cfg {Number} level (1|2|3|4|5|6) default 1
3388 * Create a new Header
3389 * @param {Object} config The config object
3393 Roo.bootstrap.Header = function(config){
3394 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3397 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3405 getAutoCreate : function(){
3410 tag: 'h' + (1 *this.level),
3411 html: this.html || ''
3423 * Ext JS Library 1.1.1
3424 * Copyright(c) 2006-2007, Ext JS, LLC.
3426 * Originally Released Under LGPL - original licence link has changed is not relivant.
3429 * <script type="text/javascript">
3433 * @class Roo.bootstrap.MenuMgr
3434 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3437 Roo.bootstrap.MenuMgr = function(){
3438 var menus, active, groups = {}, attached = false, lastShow = new Date();
3440 // private - called when first menu is created
3443 active = new Roo.util.MixedCollection();
3444 Roo.get(document).addKeyListener(27, function(){
3445 if(active.length > 0){
3453 if(active && active.length > 0){
3454 var c = active.clone();
3464 if(active.length < 1){
3465 Roo.get(document).un("mouseup", onMouseDown);
3473 var last = active.last();
3474 lastShow = new Date();
3477 Roo.get(document).on("mouseup", onMouseDown);
3482 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3483 m.parentMenu.activeChild = m;
3484 }else if(last && last.isVisible()){
3485 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3490 function onBeforeHide(m){
3492 m.activeChild.hide();
3494 if(m.autoHideTimer){
3495 clearTimeout(m.autoHideTimer);
3496 delete m.autoHideTimer;
3501 function onBeforeShow(m){
3502 var pm = m.parentMenu;
3503 if(!pm && !m.allowOtherMenus){
3505 }else if(pm && pm.activeChild && active != m){
3506 pm.activeChild.hide();
3510 // private this should really trigger on mouseup..
3511 function onMouseDown(e){
3512 Roo.log("on Mouse Up");
3514 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3515 Roo.log("MenuManager hideAll");
3524 function onBeforeCheck(mi, state){
3526 var g = groups[mi.group];
3527 for(var i = 0, l = g.length; i < l; i++){
3529 g[i].setChecked(false);
3538 * Hides all menus that are currently visible
3540 hideAll : function(){
3545 register : function(menu){
3549 menus[menu.id] = menu;
3550 menu.on("beforehide", onBeforeHide);
3551 menu.on("hide", onHide);
3552 menu.on("beforeshow", onBeforeShow);
3553 menu.on("show", onShow);
3555 if(g && menu.events["checkchange"]){
3559 groups[g].push(menu);
3560 menu.on("checkchange", onCheck);
3565 * Returns a {@link Roo.menu.Menu} object
3566 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3567 * be used to generate and return a new Menu instance.
3569 get : function(menu){
3570 if(typeof menu == "string"){ // menu id
3572 }else if(menu.events){ // menu instance
3575 /*else if(typeof menu.length == 'number'){ // array of menu items?
3576 return new Roo.bootstrap.Menu({items:menu});
3577 }else{ // otherwise, must be a config
3578 return new Roo.bootstrap.Menu(menu);
3585 unregister : function(menu){
3586 delete menus[menu.id];
3587 menu.un("beforehide", onBeforeHide);
3588 menu.un("hide", onHide);
3589 menu.un("beforeshow", onBeforeShow);
3590 menu.un("show", onShow);
3592 if(g && menu.events["checkchange"]){
3593 groups[g].remove(menu);
3594 menu.un("checkchange", onCheck);
3599 registerCheckable : function(menuItem){
3600 var g = menuItem.group;
3605 groups[g].push(menuItem);
3606 menuItem.on("beforecheckchange", onBeforeCheck);
3611 unregisterCheckable : function(menuItem){
3612 var g = menuItem.group;
3614 groups[g].remove(menuItem);
3615 menuItem.un("beforecheckchange", onBeforeCheck);
3627 * @class Roo.bootstrap.Menu
3628 * @extends Roo.bootstrap.Component
3629 * @children Roo.bootstrap.MenuItem
3630 * Bootstrap Menu class - container for MenuItems
3632 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3633 * @cfg {bool} hidden if the menu should be hidden when rendered.
3634 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3635 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3636 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3637 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3641 * @param {Object} config The config object
3645 Roo.bootstrap.Menu = function(config){
3647 if (config.type == 'treeview') {
3648 // normally menu's are drawn attached to the document to handle layering etc..
3649 // however treeview (used by the docs menu is drawn into the parent element)
3650 this.container_method = 'getChildContainer';
3653 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3654 if (this.registerMenu && this.type != 'treeview') {
3655 Roo.bootstrap.MenuMgr.register(this);
3662 * Fires before this menu is displayed (return false to block)
3663 * @param {Roo.menu.Menu} this
3668 * Fires before this menu is hidden (return false to block)
3669 * @param {Roo.menu.Menu} this
3674 * Fires after this menu is displayed
3675 * @param {Roo.menu.Menu} this
3680 * Fires after this menu is hidden
3681 * @param {Roo.menu.Menu} this
3686 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3687 * @param {Roo.menu.Menu} this
3688 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3689 * @param {Roo.EventObject} e
3694 * Fires when the mouse is hovering over this menu
3695 * @param {Roo.menu.Menu} this
3696 * @param {Roo.EventObject} e
3697 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3702 * Fires when the mouse exits this menu
3703 * @param {Roo.menu.Menu} this
3704 * @param {Roo.EventObject} e
3705 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3710 * Fires when a menu item contained in this menu is clicked
3711 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3712 * @param {Roo.EventObject} e
3716 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3719 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3723 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3726 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3728 registerMenu : true,
3730 menuItems :false, // stores the menu items..
3740 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3742 hideTrigger : false,
3747 getChildContainer : function() {
3751 getAutoCreate : function(){
3753 //if (['right'].indexOf(this.align)!==-1) {
3754 // cfg.cn[1].cls += ' pull-right'
3759 cls : 'dropdown-menu shadow' ,
3760 style : 'z-index:1000'
3764 if (this.type === 'submenu') {
3765 cfg.cls = 'submenu active';
3767 if (this.type === 'treeview') {
3768 cfg.cls = 'treeview-menu';
3773 initEvents : function() {
3775 // Roo.log("ADD event");
3776 // Roo.log(this.triggerEl.dom);
3777 if (this.triggerEl) {
3779 this.triggerEl.on('click', this.onTriggerClick, this);
3781 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3783 if (!this.hideTrigger) {
3784 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3785 // dropdown toggle on the 'a' in BS4?
3786 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3788 this.triggerEl.addClass('dropdown-toggle');
3794 this.el.on('touchstart' , this.onTouch, this);
3796 this.el.on('click' , this.onClick, this);
3798 this.el.on("mouseover", this.onMouseOver, this);
3799 this.el.on("mouseout", this.onMouseOut, this);
3803 findTargetItem : function(e)
3805 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3809 //Roo.log(t); Roo.log(t.id);
3811 //Roo.log(this.menuitems);
3812 return this.menuitems.get(t.id);
3814 //return this.items.get(t.menuItemId);
3820 onTouch : function(e)
3822 Roo.log("menu.onTouch");
3823 //e.stopEvent(); this make the user popdown broken
3827 onClick : function(e)
3829 Roo.log("menu.onClick");
3831 var t = this.findTargetItem(e);
3832 if(!t || t.isContainer){
3837 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3838 if(t == this.activeItem && t.shouldDeactivate(e)){
3839 this.activeItem.deactivate();
3840 delete this.activeItem;
3844 this.setActiveItem(t, true);
3852 Roo.log('pass click event');
3856 this.fireEvent("click", this, t, e);
3860 if(!t.href.length || t.href == '#'){
3861 (function() { _this.hide(); }).defer(100);
3866 onMouseOver : function(e){
3867 var t = this.findTargetItem(e);
3870 // if(t.canActivate && !t.disabled){
3871 // this.setActiveItem(t, true);
3875 this.fireEvent("mouseover", this, e, t);
3877 isVisible : function(){
3878 return !this.hidden;
3880 onMouseOut : function(e){
3881 var t = this.findTargetItem(e);
3884 // if(t == this.activeItem && t.shouldDeactivate(e)){
3885 // this.activeItem.deactivate();
3886 // delete this.activeItem;
3889 this.fireEvent("mouseout", this, e, t);
3894 * Displays this menu relative to another element
3895 * @param {String/HTMLElement/Roo.Element} element The element to align to
3896 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3897 * the element (defaults to this.defaultAlign)
3898 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3900 show : function(el, pos, parentMenu)
3902 if (false === this.fireEvent("beforeshow", this)) {
3903 Roo.log("show canceled");
3906 this.parentMenu = parentMenu;
3910 this.el.addClass('show'); // show otherwise we do not know how big we are..
3912 var xy = this.el.getAlignToXY(el, pos);
3914 // bl-tl << left align below
3915 // tl-bl << left align
3917 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3918 // if it goes to far to the right.. -> align left.
3919 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3922 // was left align - go right?
3923 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3926 // goes down the bottom
3927 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3929 var a = this.align.replace('?', '').split('-');
3930 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3934 this.showAt( xy , parentMenu, false);
3937 * Displays this menu at a specific xy position
3938 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3939 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3941 showAt : function(xy, parentMenu, /* private: */_e){
3942 this.parentMenu = parentMenu;
3947 this.fireEvent("beforeshow", this);
3948 //xy = this.el.adjustForConstraints(xy);
3952 this.hideMenuItems();
3953 this.hidden = false;
3954 if (this.triggerEl) {
3955 this.triggerEl.addClass('open');
3958 this.el.addClass('show');
3962 // reassign x when hitting right
3964 // reassign y when hitting bottom
3966 // but the list may align on trigger left or trigger top... should it be a properity?
3968 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3973 this.fireEvent("show", this);
3979 this.doFocus.defer(50, this);
3983 doFocus : function(){
3985 this.focusEl.focus();
3990 * Hides this menu and optionally all parent menus
3991 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3993 hide : function(deep)
3995 if (false === this.fireEvent("beforehide", this)) {
3996 Roo.log("hide canceled");
3999 this.hideMenuItems();
4000 if(this.el && this.isVisible()){
4002 if(this.activeItem){
4003 this.activeItem.deactivate();
4004 this.activeItem = null;
4006 if (this.triggerEl) {
4007 this.triggerEl.removeClass('open');
4010 this.el.removeClass('show');
4012 this.fireEvent("hide", this);
4014 if(deep === true && this.parentMenu){
4015 this.parentMenu.hide(true);
4019 onTriggerClick : function(e)
4021 Roo.log('trigger click');
4023 var target = e.getTarget();
4025 Roo.log(target.nodeName.toLowerCase());
4027 if(target.nodeName.toLowerCase() === 'i'){
4033 onTriggerPress : function(e)
4035 Roo.log('trigger press');
4036 //Roo.log(e.getTarget());
4037 // Roo.log(this.triggerEl.dom);
4039 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4040 var pel = Roo.get(e.getTarget());
4041 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4042 Roo.log('is treeview or dropdown?');
4046 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4050 if (this.isVisible()) {
4056 this.show(this.triggerEl, this.align, false);
4059 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4066 hideMenuItems : function()
4068 Roo.log("hide Menu Items");
4073 this.el.select('.open',true).each(function(aa) {
4075 aa.removeClass('open');
4079 addxtypeChild : function (tree, cntr) {
4080 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4082 this.menuitems.add(comp);
4094 this.getEl().dom.innerHTML = '';
4095 this.menuitems.clear();
4109 * @class Roo.bootstrap.MenuItem
4110 * @extends Roo.bootstrap.Component
4111 * Bootstrap MenuItem class
4112 * @cfg {String} html the menu label
4113 * @cfg {String} href the link
4114 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4115 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4116 * @cfg {Boolean} active used on sidebars to highlight active itesm
4117 * @cfg {String} fa favicon to show on left of menu item.
4118 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4122 * Create a new MenuItem
4123 * @param {Object} config The config object
4127 Roo.bootstrap.MenuItem = function(config){
4128 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4133 * The raw click event for the entire grid.
4134 * @param {Roo.bootstrap.MenuItem} this
4135 * @param {Roo.EventObject} e
4141 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4145 preventDefault: false,
4146 isContainer : false,
4150 getAutoCreate : function(){
4152 if(this.isContainer){
4155 cls: 'dropdown-menu-item '
4165 cls : 'dropdown-item',
4170 if (this.fa !== false) {
4173 cls : 'fa fa-' + this.fa
4182 cls: 'dropdown-menu-item',
4185 if (this.parent().type == 'treeview') {
4186 cfg.cls = 'treeview-menu';
4189 cfg.cls += ' active';
4194 anc.href = this.href || cfg.cn[0].href ;
4195 ctag.html = this.html || cfg.cn[0].html ;
4199 initEvents: function()
4201 if (this.parent().type == 'treeview') {
4202 this.el.select('a').on('click', this.onClick, this);
4206 this.menu.parentType = this.xtype;
4207 this.menu.triggerEl = this.el;
4208 this.menu = this.addxtype(Roo.apply({}, this.menu));
4212 onClick : function(e)
4214 Roo.log('item on click ');
4216 if(this.preventDefault){
4219 //this.parent().hideMenuItems();
4221 this.fireEvent('click', this, e);
4240 * @class Roo.bootstrap.MenuSeparator
4241 * @extends Roo.bootstrap.Component
4242 * Bootstrap MenuSeparator class
4245 * Create a new MenuItem
4246 * @param {Object} config The config object
4250 Roo.bootstrap.MenuSeparator = function(config){
4251 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4254 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4256 getAutoCreate : function(){
4275 * @class Roo.bootstrap.Modal
4276 * @extends Roo.bootstrap.Component
4279 * @children Roo.bootstrap.Component
4280 * Bootstrap Modal class
4281 * @cfg {String} title Title of dialog
4282 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4283 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4284 * @cfg {Boolean} specificTitle default false
4285 * @cfg {Array} buttons Array of buttons or standard button set..
4286 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4287 * @cfg {Boolean} animate default true
4288 * @cfg {Boolean} allow_close default true
4289 * @cfg {Boolean} fitwindow default false
4290 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4291 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4292 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4293 * @cfg {String} size (sm|lg|xl) default empty
4294 * @cfg {Number} max_width set the max width of modal
4295 * @cfg {Boolean} editableTitle can the title be edited
4300 * Create a new Modal Dialog
4301 * @param {Object} config The config object
4304 Roo.bootstrap.Modal = function(config){
4305 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4310 * The raw btnclick event for the button
4311 * @param {Roo.EventObject} e
4316 * Fire when dialog resize
4317 * @param {Roo.bootstrap.Modal} this
4318 * @param {Roo.EventObject} e
4322 * @event titlechanged
4323 * Fire when the editable title has been changed
4324 * @param {Roo.bootstrap.Modal} this
4325 * @param {Roo.EventObject} value
4327 "titlechanged" : true
4330 this.buttons = this.buttons || [];
4333 this.tmpl = Roo.factory(this.tmpl);
4338 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4340 title : 'test dialog',
4350 specificTitle: false,
4352 buttonPosition: 'right',
4374 editableTitle : false,
4376 onRender : function(ct, position)
4378 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4381 var cfg = Roo.apply({}, this.getAutoCreate());
4384 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4386 //if (!cfg.name.length) {
4390 cfg.cls += ' ' + this.cls;
4393 cfg.style = this.style;
4395 this.el = Roo.get(document.body).createChild(cfg, position);
4397 //var type = this.el.dom.type;
4400 if(this.tabIndex !== undefined){
4401 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4404 this.dialogEl = this.el.select('.modal-dialog',true).first();
4405 this.bodyEl = this.el.select('.modal-body',true).first();
4406 this.closeEl = this.el.select('.modal-header .close', true).first();
4407 this.headerEl = this.el.select('.modal-header',true).first();
4408 this.titleEl = this.el.select('.modal-title',true).first();
4409 this.footerEl = this.el.select('.modal-footer',true).first();
4411 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4413 //this.el.addClass("x-dlg-modal");
4415 if (this.buttons.length) {
4416 Roo.each(this.buttons, function(bb) {
4417 var b = Roo.apply({}, bb);
4418 b.xns = b.xns || Roo.bootstrap;
4419 b.xtype = b.xtype || 'Button';
4420 if (typeof(b.listeners) == 'undefined') {
4421 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4424 var btn = Roo.factory(b);
4426 btn.render(this.getButtonContainer());
4430 // render the children.
4433 if(typeof(this.items) != 'undefined'){
4434 var items = this.items;
4437 for(var i =0;i < items.length;i++) {
4438 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4442 this.items = nitems;
4444 // where are these used - they used to be body/close/footer
4448 //this.el.addClass([this.fieldClass, this.cls]);
4452 getAutoCreate : function()
4454 // we will default to modal-body-overflow - might need to remove or make optional later.
4456 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4457 html : this.html || ''
4462 cls : 'modal-title',
4466 if(this.specificTitle){ // WTF is this?
4471 if (this.allow_close && Roo.bootstrap.version == 3) {
4481 if (this.editableTitle) {
4483 cls: 'form-control roo-editable-title d-none',
4489 if (this.allow_close && Roo.bootstrap.version == 4) {
4499 if(this.size.length){
4500 size = 'modal-' + this.size;
4503 var footer = Roo.bootstrap.version == 3 ?
4505 cls : 'modal-footer',
4509 cls: 'btn-' + this.buttonPosition
4514 { // BS4 uses mr-auto on left buttons....
4515 cls : 'modal-footer'
4526 cls: "modal-dialog " + size,
4529 cls : "modal-content",
4532 cls : 'modal-header',
4547 modal.cls += ' fade';
4553 getChildContainer : function() {
4558 getButtonContainer : function() {
4560 return Roo.bootstrap.version == 4 ?
4561 this.el.select('.modal-footer',true).first()
4562 : this.el.select('.modal-footer div',true).first();
4565 initEvents : function()
4567 if (this.allow_close) {
4568 this.closeEl.on('click', this.hide, this);
4570 Roo.EventManager.onWindowResize(this.resize, this, true);
4571 if (this.editableTitle) {
4572 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4573 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4574 this.headerEditEl.on('keyup', function(e) {
4575 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4576 this.toggleHeaderInput(false)
4579 this.headerEditEl.on('blur', function(e) {
4580 this.toggleHeaderInput(false)
4589 this.maskEl.setSize(
4590 Roo.lib.Dom.getViewWidth(true),
4591 Roo.lib.Dom.getViewHeight(true)
4594 if (this.fitwindow) {
4596 this.dialogEl.setStyle( { 'max-width' : '100%' });
4598 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4599 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4604 if(this.max_width !== 0) {
4606 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4609 this.setSize(w, this.height);
4613 if(this.max_height) {
4614 this.setSize(w,Math.min(
4616 Roo.lib.Dom.getViewportHeight(true) - 60
4622 if(!this.fit_content) {
4623 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4627 this.setSize(w, Math.min(
4629 this.headerEl.getHeight() +
4630 this.footerEl.getHeight() +
4631 this.getChildHeight(this.bodyEl.dom.childNodes),
4632 Roo.lib.Dom.getViewportHeight(true) - 60)
4638 setSize : function(w,h)
4649 if (!this.rendered) {
4652 this.toggleHeaderInput(false);
4653 //this.el.setStyle('display', 'block');
4654 this.el.removeClass('hideing');
4655 this.el.dom.style.display='block';
4657 Roo.get(document.body).addClass('modal-open');
4659 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4662 this.el.addClass('show');
4663 this.el.addClass('in');
4666 this.el.addClass('show');
4667 this.el.addClass('in');
4670 // not sure how we can show data in here..
4672 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4675 Roo.get(document.body).addClass("x-body-masked");
4677 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4678 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4679 this.maskEl.dom.style.display = 'block';
4680 this.maskEl.addClass('show');
4685 this.fireEvent('show', this);
4687 // set zindex here - otherwise it appears to be ignored...
4688 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4691 this.items.forEach( function(e) {
4692 e.layout ? e.layout() : false;
4700 if(this.fireEvent("beforehide", this) !== false){
4702 this.maskEl.removeClass('show');
4704 this.maskEl.dom.style.display = '';
4705 Roo.get(document.body).removeClass("x-body-masked");
4706 this.el.removeClass('in');
4707 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4709 if(this.animate){ // why
4710 this.el.addClass('hideing');
4711 this.el.removeClass('show');
4713 if (!this.el.hasClass('hideing')) {
4714 return; // it's been shown again...
4717 this.el.dom.style.display='';
4719 Roo.get(document.body).removeClass('modal-open');
4720 this.el.removeClass('hideing');
4724 this.el.removeClass('show');
4725 this.el.dom.style.display='';
4726 Roo.get(document.body).removeClass('modal-open');
4729 this.fireEvent('hide', this);
4732 isVisible : function()
4735 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4739 addButton : function(str, cb)
4743 var b = Roo.apply({}, { html : str } );
4744 b.xns = b.xns || Roo.bootstrap;
4745 b.xtype = b.xtype || 'Button';
4746 if (typeof(b.listeners) == 'undefined') {
4747 b.listeners = { click : cb.createDelegate(this) };
4750 var btn = Roo.factory(b);
4752 btn.render(this.getButtonContainer());
4758 setDefaultButton : function(btn)
4760 //this.el.select('.modal-footer').()
4763 resizeTo: function(w,h)
4765 this.dialogEl.setWidth(w);
4767 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4769 this.bodyEl.setHeight(h - diff);
4771 this.fireEvent('resize', this);
4774 setContentSize : function(w, h)
4778 onButtonClick: function(btn,e)
4781 this.fireEvent('btnclick', btn.name, e);
4784 * Set the title of the Dialog
4785 * @param {String} str new Title
4787 setTitle: function(str) {
4788 this.titleEl.dom.innerHTML = str;
4792 * Set the body of the Dialog
4793 * @param {String} str new Title
4795 setBody: function(str) {
4796 this.bodyEl.dom.innerHTML = str;
4799 * Set the body of the Dialog using the template
4800 * @param {Obj} data - apply this data to the template and replace the body contents.
4802 applyBody: function(obj)
4805 Roo.log("Error - using apply Body without a template");
4808 this.tmpl.overwrite(this.bodyEl, obj);
4811 getChildHeight : function(child_nodes)
4815 child_nodes.length == 0
4820 var child_height = 0;
4822 for(var i = 0; i < child_nodes.length; i++) {
4825 * for modal with tabs...
4826 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4828 var layout_childs = child_nodes[i].childNodes;
4830 for(var j = 0; j < layout_childs.length; j++) {
4832 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4834 var layout_body_childs = layout_childs[j].childNodes;
4836 for(var k = 0; k < layout_body_childs.length; k++) {
4838 if(layout_body_childs[k].classList.contains('navbar')) {
4839 child_height += layout_body_childs[k].offsetHeight;
4843 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4845 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4847 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4849 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4850 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4865 child_height += child_nodes[i].offsetHeight;
4866 // Roo.log(child_nodes[i].offsetHeight);
4869 return child_height;
4871 toggleHeaderInput : function(is_edit)
4873 if (!this.editableTitle) {
4874 return; // not editable.
4876 if (is_edit && this.is_header_editing) {
4877 return; // already editing..
4881 this.headerEditEl.dom.value = this.title;
4882 this.headerEditEl.removeClass('d-none');
4883 this.headerEditEl.dom.focus();
4884 this.titleEl.addClass('d-none');
4886 this.is_header_editing = true;
4889 // flip back to not editing.
4890 this.title = this.headerEditEl.dom.value;
4891 this.headerEditEl.addClass('d-none');
4892 this.titleEl.removeClass('d-none');
4893 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4894 this.is_header_editing = false;
4895 this.fireEvent('titlechanged', this, this.title);
4904 Roo.apply(Roo.bootstrap.Modal, {
4906 * Button config that displays a single OK button
4915 * Button config that displays Yes and No buttons
4931 * Button config that displays OK and Cancel buttons
4946 * Button config that displays Yes, No and Cancel buttons
4971 * messagebox - can be used as a replace
4975 * @class Roo.MessageBox
4976 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4980 Roo.Msg.alert('Status', 'Changes saved successfully.');
4982 // Prompt for user data:
4983 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4985 // process text value...
4989 // Show a dialog using config options:
4991 title:'Save Changes?',
4992 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4993 buttons: Roo.Msg.YESNOCANCEL,
5000 Roo.bootstrap.MessageBox = function(){
5001 var dlg, opt, mask, waitTimer;
5002 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
5003 var buttons, activeTextEl, bwidth;
5007 var handleButton = function(button){
5009 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5013 var handleHide = function(){
5015 dlg.el.removeClass(opt.cls);
5018 // Roo.TaskMgr.stop(waitTimer);
5019 // waitTimer = null;
5024 var updateButtons = function(b){
5027 buttons["ok"].hide();
5028 buttons["cancel"].hide();
5029 buttons["yes"].hide();
5030 buttons["no"].hide();
5031 dlg.footerEl.hide();
5035 dlg.footerEl.show();
5036 for(var k in buttons){
5037 if(typeof buttons[k] != "function"){
5040 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5041 width += buttons[k].el.getWidth()+15;
5051 var handleEsc = function(d, k, e){
5052 if(opt && opt.closable !== false){
5062 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5063 * @return {Roo.BasicDialog} The BasicDialog element
5065 getDialog : function(){
5067 dlg = new Roo.bootstrap.Modal( {
5070 //constraintoviewport:false,
5072 //collapsible : false,
5077 //buttonAlign:"center",
5078 closeClick : function(){
5079 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5082 handleButton("cancel");
5087 dlg.on("hide", handleHide);
5089 //dlg.addKeyListener(27, handleEsc);
5091 this.buttons = buttons;
5092 var bt = this.buttonText;
5093 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5094 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5095 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5096 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5098 bodyEl = dlg.bodyEl.createChild({
5100 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5101 '<textarea class="roo-mb-textarea"></textarea>' +
5102 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5104 msgEl = bodyEl.dom.firstChild;
5105 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5106 textboxEl.enableDisplayMode();
5107 textboxEl.addKeyListener([10,13], function(){
5108 if(dlg.isVisible() && opt && opt.buttons){
5111 }else if(opt.buttons.yes){
5112 handleButton("yes");
5116 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5117 textareaEl.enableDisplayMode();
5118 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5119 progressEl.enableDisplayMode();
5121 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5122 var pf = progressEl.dom.firstChild;
5124 pp = Roo.get(pf.firstChild);
5125 pp.setHeight(pf.offsetHeight);
5133 * Updates the message box body text
5134 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5135 * the XHTML-compliant non-breaking space character '&#160;')
5136 * @return {Roo.MessageBox} This message box
5138 updateText : function(text)
5140 if(!dlg.isVisible() && !opt.width){
5141 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5142 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5144 msgEl.innerHTML = text || ' ';
5146 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5147 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5149 Math.min(opt.width || cw , this.maxWidth),
5150 Math.max(opt.minWidth || this.minWidth, bwidth)
5153 activeTextEl.setWidth(w);
5155 if(dlg.isVisible()){
5156 dlg.fixedcenter = false;
5158 // to big, make it scroll. = But as usual stupid IE does not support
5161 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5162 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5163 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5165 bodyEl.dom.style.height = '';
5166 bodyEl.dom.style.overflowY = '';
5169 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5171 bodyEl.dom.style.overflowX = '';
5174 dlg.setContentSize(w, bodyEl.getHeight());
5175 if(dlg.isVisible()){
5176 dlg.fixedcenter = true;
5182 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5183 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5184 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5185 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5186 * @return {Roo.MessageBox} This message box
5188 updateProgress : function(value, text){
5190 this.updateText(text);
5193 if (pp) { // weird bug on my firefox - for some reason this is not defined
5194 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5195 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5201 * Returns true if the message box is currently displayed
5202 * @return {Boolean} True if the message box is visible, else false
5204 isVisible : function(){
5205 return dlg && dlg.isVisible();
5209 * Hides the message box if it is displayed
5212 if(this.isVisible()){
5218 * Displays a new message box, or reinitializes an existing message box, based on the config options
5219 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5220 * The following config object properties are supported:
5222 Property Type Description
5223 ---------- --------------- ------------------------------------------------------------------------------------
5224 animEl String/Element An id or Element from which the message box should animate as it opens and
5225 closes (defaults to undefined)
5226 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5227 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5228 closable Boolean False to hide the top-right close button (defaults to true). Note that
5229 progress and wait dialogs will ignore this property and always hide the
5230 close button as they can only be closed programmatically.
5231 cls String A custom CSS class to apply to the message box element
5232 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5233 displayed (defaults to 75)
5234 fn Function A callback function to execute after closing the dialog. The arguments to the
5235 function will be btn (the name of the button that was clicked, if applicable,
5236 e.g. "ok"), and text (the value of the active text field, if applicable).
5237 Progress and wait dialogs will ignore this option since they do not respond to
5238 user actions and can only be closed programmatically, so any required function
5239 should be called by the same code after it closes the dialog.
5240 icon String A CSS class that provides a background image to be used as an icon for
5241 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5242 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5243 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5244 modal Boolean False to allow user interaction with the page while the message box is
5245 displayed (defaults to true)
5246 msg String A string that will replace the existing message box body text (defaults
5247 to the XHTML-compliant non-breaking space character ' ')
5248 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5249 progress Boolean True to display a progress bar (defaults to false)
5250 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5251 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5252 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5253 title String The title text
5254 value String The string value to set into the active textbox element if displayed
5255 wait Boolean True to display a progress bar (defaults to false)
5256 width Number The width of the dialog in pixels
5263 msg: 'Please enter your address:',
5265 buttons: Roo.MessageBox.OKCANCEL,
5268 animEl: 'addAddressBtn'
5271 * @param {Object} config Configuration options
5272 * @return {Roo.MessageBox} This message box
5274 show : function(options)
5277 // this causes nightmares if you show one dialog after another
5278 // especially on callbacks..
5280 if(this.isVisible()){
5283 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5284 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5285 Roo.log("New Dialog Message:" + options.msg )
5286 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5287 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5290 var d = this.getDialog();
5292 d.setTitle(opt.title || " ");
5293 d.closeEl.setDisplayed(opt.closable !== false);
5294 activeTextEl = textboxEl;
5295 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5300 textareaEl.setHeight(typeof opt.multiline == "number" ?
5301 opt.multiline : this.defaultTextHeight);
5302 activeTextEl = textareaEl;
5311 progressEl.setDisplayed(opt.progress === true);
5313 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5315 this.updateProgress(0);
5316 activeTextEl.dom.value = opt.value || "";
5318 dlg.setDefaultButton(activeTextEl);
5320 var bs = opt.buttons;
5324 }else if(bs && bs.yes){
5325 db = buttons["yes"];
5327 dlg.setDefaultButton(db);
5329 bwidth = updateButtons(opt.buttons);
5330 this.updateText(opt.msg);
5332 d.el.addClass(opt.cls);
5334 d.proxyDrag = opt.proxyDrag === true;
5335 d.modal = opt.modal !== false;
5336 d.mask = opt.modal !== false ? mask : false;
5338 // force it to the end of the z-index stack so it gets a cursor in FF
5339 document.body.appendChild(dlg.el.dom);
5340 d.animateTarget = null;
5341 d.show(options.animEl);
5347 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5348 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5349 * and closing the message box when the process is complete.
5350 * @param {String} title The title bar text
5351 * @param {String} msg The message box body text
5352 * @return {Roo.MessageBox} This message box
5354 progress : function(title, msg){
5361 minWidth: this.minProgressWidth,
5368 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5369 * If a callback function is passed it will be called after the user clicks the button, and the
5370 * id of the button that was clicked will be passed as the only parameter to the callback
5371 * (could also be the top-right close button).
5372 * @param {String} title The title bar text
5373 * @param {String} msg The message box body text
5374 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5375 * @param {Object} scope (optional) The scope of the callback function
5376 * @return {Roo.MessageBox} This message box
5378 alert : function(title, msg, fn, scope)
5393 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5394 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5395 * You are responsible for closing the message box when the process is complete.
5396 * @param {String} msg The message box body text
5397 * @param {String} title (optional) The title bar text
5398 * @return {Roo.MessageBox} This message box
5400 wait : function(msg, title){
5411 waitTimer = Roo.TaskMgr.start({
5413 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5421 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5422 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5423 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5424 * @param {String} title The title bar text
5425 * @param {String} msg The message box body text
5426 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5427 * @param {Object} scope (optional) The scope of the callback function
5428 * @return {Roo.MessageBox} This message box
5430 confirm : function(title, msg, fn, scope){
5434 buttons: this.YESNO,
5443 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5444 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5445 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5446 * (could also be the top-right close button) and the text that was entered will be passed as the two
5447 * parameters to the callback.
5448 * @param {String} title The title bar text
5449 * @param {String} msg The message box body text
5450 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5451 * @param {Object} scope (optional) The scope of the callback function
5452 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5453 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5454 * @return {Roo.MessageBox} This message box
5456 prompt : function(title, msg, fn, scope, multiline){
5460 buttons: this.OKCANCEL,
5465 multiline: multiline,
5472 * Button config that displays a single OK button
5477 * Button config that displays Yes and No buttons
5480 YESNO : {yes:true, no:true},
5482 * Button config that displays OK and Cancel buttons
5485 OKCANCEL : {ok:true, cancel:true},
5487 * Button config that displays Yes, No and Cancel buttons
5490 YESNOCANCEL : {yes:true, no:true, cancel:true},
5493 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5496 defaultTextHeight : 75,
5498 * The maximum width in pixels of the message box (defaults to 600)
5503 * The minimum width in pixels of the message box (defaults to 100)
5508 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5509 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5512 minProgressWidth : 250,
5514 * An object containing the default button text strings that can be overriden for localized language support.
5515 * Supported properties are: ok, cancel, yes and no.
5516 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5529 * Shorthand for {@link Roo.MessageBox}
5531 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5532 Roo.Msg = Roo.Msg || Roo.MessageBox;
5541 * @class Roo.bootstrap.Navbar
5542 * @extends Roo.bootstrap.Component
5543 * Bootstrap Navbar class
5546 * Create a new Navbar
5547 * @param {Object} config The config object
5551 Roo.bootstrap.Navbar = function(config){
5552 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5556 * @event beforetoggle
5557 * Fire before toggle the menu
5558 * @param {Roo.EventObject} e
5560 "beforetoggle" : true
5564 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5573 getAutoCreate : function(){
5576 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5580 initEvents :function ()
5582 //Roo.log(this.el.select('.navbar-toggle',true));
5583 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5590 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5592 var size = this.el.getSize();
5593 this.maskEl.setSize(size.width, size.height);
5594 this.maskEl.enableDisplayMode("block");
5603 getChildContainer : function()
5605 if (this.el && this.el.select('.collapse').getCount()) {
5606 return this.el.select('.collapse',true).first();
5621 onToggle : function()
5624 if(this.fireEvent('beforetoggle', this) === false){
5627 var ce = this.el.select('.navbar-collapse',true).first();
5629 if (!ce.hasClass('show')) {
5639 * Expand the navbar pulldown
5641 expand : function ()
5644 var ce = this.el.select('.navbar-collapse',true).first();
5645 if (ce.hasClass('collapsing')) {
5648 ce.dom.style.height = '';
5650 ce.addClass('in'); // old...
5651 ce.removeClass('collapse');
5652 ce.addClass('show');
5653 var h = ce.getHeight();
5655 ce.removeClass('show');
5656 // at this point we should be able to see it..
5657 ce.addClass('collapsing');
5659 ce.setHeight(0); // resize it ...
5660 ce.on('transitionend', function() {
5661 //Roo.log('done transition');
5662 ce.removeClass('collapsing');
5663 ce.addClass('show');
5664 ce.removeClass('collapse');
5666 ce.dom.style.height = '';
5667 }, this, { single: true} );
5669 ce.dom.scrollTop = 0;
5672 * Collapse the navbar pulldown
5674 collapse : function()
5676 var ce = this.el.select('.navbar-collapse',true).first();
5678 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5679 // it's collapsed or collapsing..
5682 ce.removeClass('in'); // old...
5683 ce.setHeight(ce.getHeight());
5684 ce.removeClass('show');
5685 ce.addClass('collapsing');
5687 ce.on('transitionend', function() {
5688 ce.dom.style.height = '';
5689 ce.removeClass('collapsing');
5690 ce.addClass('collapse');
5691 }, this, { single: true} );
5711 * @class Roo.bootstrap.NavSimplebar
5712 * @extends Roo.bootstrap.Navbar
5713 * Bootstrap Sidebar class
5715 * @cfg {Boolean} inverse is inverted color
5717 * @cfg {String} type (nav | pills | tabs)
5718 * @cfg {Boolean} arrangement stacked | justified
5719 * @cfg {String} align (left | right) alignment
5721 * @cfg {Boolean} main (true|false) main nav bar? default false
5722 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5724 * @cfg {String} tag (header|footer|nav|div) default is nav
5726 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5730 * Create a new Sidebar
5731 * @param {Object} config The config object
5735 Roo.bootstrap.NavSimplebar = function(config){
5736 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5739 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5755 getAutoCreate : function(){
5759 tag : this.tag || 'div',
5760 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5762 if (['light','white'].indexOf(this.weight) > -1) {
5763 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5765 cfg.cls += ' bg-' + this.weight;
5768 cfg.cls += ' navbar-inverse';
5772 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5774 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5783 cls: 'nav nav-' + this.xtype,
5789 this.type = this.type || 'nav';
5790 if (['tabs','pills'].indexOf(this.type) != -1) {
5791 cfg.cn[0].cls += ' nav-' + this.type
5795 if (this.type!=='nav') {
5796 Roo.log('nav type must be nav/tabs/pills')
5798 cfg.cn[0].cls += ' navbar-nav'
5804 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5805 cfg.cn[0].cls += ' nav-' + this.arrangement;
5809 if (this.align === 'right') {
5810 cfg.cn[0].cls += ' navbar-right';
5835 * navbar-expand-md fixed-top
5839 * @class Roo.bootstrap.NavHeaderbar
5840 * @extends Roo.bootstrap.NavSimplebar
5841 * Bootstrap Sidebar class
5843 * @cfg {String} brand what is brand
5844 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5845 * @cfg {String} brand_href href of the brand
5846 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5847 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5848 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5849 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5852 * Create a new Sidebar
5853 * @param {Object} config The config object
5857 Roo.bootstrap.NavHeaderbar = function(config){
5858 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5862 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5869 desktopCenter : false,
5872 getAutoCreate : function(){
5875 tag: this.nav || 'nav',
5876 cls: 'navbar navbar-expand-md',
5882 if (this.desktopCenter) {
5883 cn.push({cls : 'container', cn : []});
5891 cls: 'navbar-toggle navbar-toggler',
5892 'data-toggle': 'collapse',
5897 html: 'Toggle navigation'
5901 cls: 'icon-bar navbar-toggler-icon'
5914 cn.push( Roo.bootstrap.version == 4 ? btn : {
5916 cls: 'navbar-header',
5925 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5929 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5931 if (['light','white'].indexOf(this.weight) > -1) {
5932 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5934 cfg.cls += ' bg-' + this.weight;
5937 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5938 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5940 // tag can override this..
5942 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5945 if (this.brand !== '') {
5946 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5947 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5949 href: this.brand_href ? this.brand_href : '#',
5950 cls: 'navbar-brand',
5958 cfg.cls += ' main-nav';
5966 getHeaderChildContainer : function()
5968 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5969 return this.el.select('.navbar-header',true).first();
5972 return this.getChildContainer();
5975 getChildContainer : function()
5978 return this.el.select('.roo-navbar-collapse',true).first();
5983 initEvents : function()
5985 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5987 if (this.autohide) {
5992 Roo.get(document).on('scroll',function(e) {
5993 var ns = Roo.get(document).getScroll().top;
5994 var os = prevScroll;
5998 ft.removeClass('slideDown');
5999 ft.addClass('slideUp');
6002 ft.removeClass('slideUp');
6003 ft.addClass('slideDown');
6024 * @class Roo.bootstrap.NavSidebar
6025 * @extends Roo.bootstrap.Navbar
6026 * Bootstrap Sidebar class
6029 * Create a new Sidebar
6030 * @param {Object} config The config object
6034 Roo.bootstrap.NavSidebar = function(config){
6035 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6038 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
6040 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6042 getAutoCreate : function(){
6047 cls: 'sidebar sidebar-nav'
6069 * @class Roo.bootstrap.NavGroup
6070 * @extends Roo.bootstrap.Component
6071 * Bootstrap NavGroup class
6072 * @cfg {String} align (left|right)
6073 * @cfg {Boolean} inverse
6074 * @cfg {String} type (nav|pills|tab) default nav
6075 * @cfg {String} navId - reference Id for navbar.
6076 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6079 * Create a new nav group
6080 * @param {Object} config The config object
6083 Roo.bootstrap.NavGroup = function(config){
6084 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6087 Roo.bootstrap.NavGroup.register(this);
6091 * Fires when the active item changes
6092 * @param {Roo.bootstrap.NavGroup} this
6093 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6094 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6101 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6113 getAutoCreate : function()
6115 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6121 if (Roo.bootstrap.version == 4) {
6122 if (['tabs','pills'].indexOf(this.type) != -1) {
6123 cfg.cls += ' nav-' + this.type;
6125 // trying to remove so header bar can right align top?
6126 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6127 // do not use on header bar...
6128 cfg.cls += ' navbar-nav';
6133 if (['tabs','pills'].indexOf(this.type) != -1) {
6134 cfg.cls += ' nav-' + this.type
6136 if (this.type !== 'nav') {
6137 Roo.log('nav type must be nav/tabs/pills')
6139 cfg.cls += ' navbar-nav'
6143 if (this.parent() && this.parent().sidebar) {
6146 cls: 'dashboard-menu sidebar-menu'
6152 if (this.form === true) {
6155 cls: 'navbar-form form-inline'
6157 //nav navbar-right ml-md-auto
6158 if (this.align === 'right') {
6159 cfg.cls += ' navbar-right ml-md-auto';
6161 cfg.cls += ' navbar-left';
6165 if (this.align === 'right') {
6166 cfg.cls += ' navbar-right ml-md-auto';
6168 cfg.cls += ' mr-auto';
6172 cfg.cls += ' navbar-inverse';
6180 * sets the active Navigation item
6181 * @param {Roo.bootstrap.NavItem} the new current navitem
6183 setActiveItem : function(item)
6186 Roo.each(this.navItems, function(v){
6191 v.setActive(false, true);
6198 item.setActive(true, true);
6199 this.fireEvent('changed', this, item, prev);
6204 * gets the active Navigation item
6205 * @return {Roo.bootstrap.NavItem} the current navitem
6207 getActive : function()
6211 Roo.each(this.navItems, function(v){
6222 indexOfNav : function()
6226 Roo.each(this.navItems, function(v,i){
6237 * adds a Navigation item
6238 * @param {Roo.bootstrap.NavItem} the navitem to add
6240 addItem : function(cfg)
6242 if (this.form && Roo.bootstrap.version == 4) {
6245 var cn = new Roo.bootstrap.NavItem(cfg);
6247 cn.parentId = this.id;
6248 cn.onRender(this.el, null);
6252 * register a Navigation item
6253 * @param {Roo.bootstrap.NavItem} the navitem to add
6255 register : function(item)
6257 this.navItems.push( item);
6258 item.navId = this.navId;
6263 * clear all the Navigation item
6266 clearAll : function()
6269 this.el.dom.innerHTML = '';
6272 getNavItem: function(tabId)
6275 Roo.each(this.navItems, function(e) {
6276 if (e.tabId == tabId) {
6286 setActiveNext : function()
6288 var i = this.indexOfNav(this.getActive());
6289 if (i > this.navItems.length) {
6292 this.setActiveItem(this.navItems[i+1]);
6294 setActivePrev : function()
6296 var i = this.indexOfNav(this.getActive());
6300 this.setActiveItem(this.navItems[i-1]);
6302 clearWasActive : function(except) {
6303 Roo.each(this.navItems, function(e) {
6304 if (e.tabId != except.tabId && e.was_active) {
6305 e.was_active = false;
6312 getWasActive : function ()
6315 Roo.each(this.navItems, function(e) {
6330 Roo.apply(Roo.bootstrap.NavGroup, {
6334 * register a Navigation Group
6335 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6337 register : function(navgrp)
6339 this.groups[navgrp.navId] = navgrp;
6343 * fetch a Navigation Group based on the navigation ID
6344 * @param {string} the navgroup to add
6345 * @returns {Roo.bootstrap.NavGroup} the navgroup
6347 get: function(navId) {
6348 if (typeof(this.groups[navId]) == 'undefined') {
6350 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6352 return this.groups[navId] ;
6367 * @class Roo.bootstrap.NavItem
6368 * @extends Roo.bootstrap.Component
6369 * Bootstrap Navbar.NavItem class
6370 * @cfg {String} href link to
6371 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6372 * @cfg {Boolean} button_outline show and outlined button
6373 * @cfg {String} html content of button
6374 * @cfg {String} badge text inside badge
6375 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6376 * @cfg {String} glyphicon DEPRICATED - use fa
6377 * @cfg {String} icon DEPRICATED - use fa
6378 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6379 * @cfg {Boolean} active Is item active
6380 * @cfg {Boolean} disabled Is item disabled
6381 * @cfg {String} linkcls Link Class
6382 * @cfg {Boolean} preventDefault (true | false) default false
6383 * @cfg {String} tabId the tab that this item activates.
6384 * @cfg {String} tagtype (a|span) render as a href or span?
6385 * @cfg {Boolean} animateRef (true|false) link to element default false
6386 * @cfg {Roo.bootstrap.Menu} menu a Menu
6389 * Create a new Navbar Item
6390 * @param {Object} config The config object
6392 Roo.bootstrap.NavItem = function(config){
6393 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6398 * The raw click event for the entire grid.
6399 * @param {Roo.EventObject} e
6404 * Fires when the active item active state changes
6405 * @param {Roo.bootstrap.NavItem} this
6406 * @param {boolean} state the new state
6412 * Fires when scroll to element
6413 * @param {Roo.bootstrap.NavItem} this
6414 * @param {Object} options
6415 * @param {Roo.EventObject} e
6423 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6432 preventDefault : false,
6440 button_outline : false,
6444 getAutoCreate : function(){
6451 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6454 cfg.cls += ' active' ;
6456 if (this.disabled) {
6457 cfg.cls += ' disabled';
6461 if (this.button_weight.length) {
6462 cfg.tag = this.href ? 'a' : 'button';
6463 cfg.html = this.html || '';
6464 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6466 cfg.href = this.href;
6469 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6471 cfg.cls += " nav-html";
6474 // menu .. should add dropdown-menu class - so no need for carat..
6476 if (this.badge !== '') {
6478 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6483 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6487 href : this.href || "#",
6488 html: this.html || '',
6492 if (this.tagtype == 'a') {
6493 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6497 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6498 } else if (this.fa) {
6499 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6500 } else if(this.glyphicon) {
6501 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6503 cfg.cn[0].cls += " nav-html";
6507 cfg.cn[0].html += " <span class='caret'></span>";
6511 if (this.badge !== '') {
6512 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6520 onRender : function(ct, position)
6522 // Roo.log("Call onRender: " + this.xtype);
6523 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6527 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6528 this.navLink = this.el.select('.nav-link',true).first();
6529 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6534 initEvents: function()
6536 if (typeof (this.menu) != 'undefined') {
6537 this.menu.parentType = this.xtype;
6538 this.menu.triggerEl = this.el;
6539 this.menu = this.addxtype(Roo.apply({}, this.menu));
6542 this.el.on('click', this.onClick, this);
6544 //if(this.tagtype == 'span'){
6545 // this.el.select('span',true).on('click', this.onClick, this);
6548 // at this point parent should be available..
6549 this.parent().register(this);
6552 onClick : function(e)
6554 if (e.getTarget('.dropdown-menu-item')) {
6555 // did you click on a menu itemm.... - then don't trigger onclick..
6560 this.preventDefault ||
6563 Roo.log("NavItem - prevent Default?");
6567 if (this.disabled) {
6571 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6572 if (tg && tg.transition) {
6573 Roo.log("waiting for the transitionend");
6579 //Roo.log("fire event clicked");
6580 if(this.fireEvent('click', this, e) === false){
6584 if(this.tagtype == 'span'){
6588 //Roo.log(this.href);
6589 var ael = this.el.select('a',true).first();
6592 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6593 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6594 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6595 return; // ignore... - it's a 'hash' to another page.
6597 Roo.log("NavItem - prevent Default?");
6599 this.scrollToElement(e);
6603 var p = this.parent();
6605 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6606 if (typeof(p.setActiveItem) !== 'undefined') {
6607 p.setActiveItem(this);
6611 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6612 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6613 // remove the collapsed menu expand...
6614 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6618 isActive: function () {
6621 setActive : function(state, fire, is_was_active)
6623 if (this.active && !state && this.navId) {
6624 this.was_active = true;
6625 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6627 nv.clearWasActive(this);
6631 this.active = state;
6634 this.el.removeClass('active');
6635 this.navLink ? this.navLink.removeClass('active') : false;
6636 } else if (!this.el.hasClass('active')) {
6638 this.el.addClass('active');
6639 if (Roo.bootstrap.version == 4 && this.navLink ) {
6640 this.navLink.addClass('active');
6645 this.fireEvent('changed', this, state);
6648 // show a panel if it's registered and related..
6650 if (!this.navId || !this.tabId || !state || is_was_active) {
6654 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6658 var pan = tg.getPanelByName(this.tabId);
6662 // if we can not flip to new panel - go back to old nav highlight..
6663 if (false == tg.showPanel(pan)) {
6664 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6666 var onav = nv.getWasActive();
6668 onav.setActive(true, false, true);
6677 // this should not be here...
6678 setDisabled : function(state)
6680 this.disabled = state;
6682 this.el.removeClass('disabled');
6683 } else if (!this.el.hasClass('disabled')) {
6684 this.el.addClass('disabled');
6690 * Fetch the element to display the tooltip on.
6691 * @return {Roo.Element} defaults to this.el
6693 tooltipEl : function()
6695 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6698 scrollToElement : function(e)
6700 var c = document.body;
6703 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6705 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6706 c = document.documentElement;
6709 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6715 var o = target.calcOffsetsTo(c);
6722 this.fireEvent('scrollto', this, options, e);
6724 Roo.get(c).scrollTo('top', options.value, true);
6729 * Set the HTML (text content) of the item
6730 * @param {string} html content for the nav item
6732 setHtml : function(html)
6735 this.htmlEl.dom.innerHTML = html;
6747 * <span> icon </span>
6748 * <span> text </span>
6749 * <span>badge </span>
6753 * @class Roo.bootstrap.NavSidebarItem
6754 * @extends Roo.bootstrap.NavItem
6755 * Bootstrap Navbar.NavSidebarItem class
6756 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6757 * {Boolean} open is the menu open
6758 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6759 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6760 * {String} buttonSize (sm|md|lg)the extra classes for the button
6761 * {Boolean} showArrow show arrow next to the text (default true)
6763 * Create a new Navbar Button
6764 * @param {Object} config The config object
6766 Roo.bootstrap.NavSidebarItem = function(config){
6767 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6772 * The raw click event for the entire grid.
6773 * @param {Roo.EventObject} e
6778 * Fires when the active item active state changes
6779 * @param {Roo.bootstrap.NavSidebarItem} this
6780 * @param {boolean} state the new state
6788 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6790 badgeWeight : 'default',
6796 buttonWeight : 'default',
6802 getAutoCreate : function(){
6807 href : this.href || '#',
6813 if(this.buttonView){
6816 href : this.href || '#',
6817 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6830 cfg.cls += ' active';
6833 if (this.disabled) {
6834 cfg.cls += ' disabled';
6837 cfg.cls += ' open x-open';
6840 if (this.glyphicon || this.icon) {
6841 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6842 a.cn.push({ tag : 'i', cls : c }) ;
6845 if(!this.buttonView){
6848 html : this.html || ''
6855 if (this.badge !== '') {
6856 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6862 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6865 a.cls += ' dropdown-toggle treeview' ;
6871 initEvents : function()
6873 if (typeof (this.menu) != 'undefined') {
6874 this.menu.parentType = this.xtype;
6875 this.menu.triggerEl = this.el;
6876 this.menu = this.addxtype(Roo.apply({}, this.menu));
6879 this.el.on('click', this.onClick, this);
6881 if(this.badge !== ''){
6882 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6887 onClick : function(e)
6894 if(this.preventDefault){
6898 this.fireEvent('click', this, e);
6901 disable : function()
6903 this.setDisabled(true);
6908 this.setDisabled(false);
6911 setDisabled : function(state)
6913 if(this.disabled == state){
6917 this.disabled = state;
6920 this.el.addClass('disabled');
6924 this.el.removeClass('disabled');
6929 setActive : function(state)
6931 if(this.active == state){
6935 this.active = state;
6938 this.el.addClass('active');
6942 this.el.removeClass('active');
6947 isActive: function ()
6952 setBadge : function(str)
6958 this.badgeEl.dom.innerHTML = str;
6973 Roo.namespace('Roo.bootstrap.breadcrumb');
6977 * @class Roo.bootstrap.breadcrumb.Nav
6978 * @extends Roo.bootstrap.Component
6979 * Bootstrap Breadcrumb Nav Class
6981 * @children Roo.bootstrap.breadcrumb.Item
6984 * Create a new breadcrumb.Nav
6985 * @param {Object} config The config object
6989 Roo.bootstrap.breadcrumb.Nav = function(config){
6990 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6995 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6997 getAutoCreate : function()
7014 initEvents: function()
7016 this.olEl = this.el.select('ol',true).first();
7018 getChildContainer : function()
7034 * @class Roo.bootstrap.breadcrumb.Nav
7035 * @extends Roo.bootstrap.Component
7036 * Bootstrap Breadcrumb Nav Class
7038 * @children Roo.bootstrap.breadcrumb.Component
7039 * @cfg {String} html the content of the link.
7040 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7041 * @cfg {Boolean} active is it active
7045 * Create a new breadcrumb.Nav
7046 * @param {Object} config The config object
7049 Roo.bootstrap.breadcrumb.Item = function(config){
7050 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7055 * The img click event for the img.
7056 * @param {Roo.EventObject} e
7063 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7068 getAutoCreate : function()
7073 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7075 if (this.href !== false) {
7082 cfg.html = this.html;
7088 initEvents: function()
7091 this.el.select('a', true).first().on('click',this.onClick, this)
7095 onClick : function(e)
7098 this.fireEvent('click',this, e);
7111 * @class Roo.bootstrap.Row
7112 * @extends Roo.bootstrap.Component
7113 * @children Roo.bootstrap.Component
7114 * Bootstrap Row class (contains columns...)
7118 * @param {Object} config The config object
7121 Roo.bootstrap.Row = function(config){
7122 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7125 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7127 getAutoCreate : function(){
7146 * @class Roo.bootstrap.Pagination
7147 * @extends Roo.bootstrap.Component
7148 * Bootstrap Pagination class
7149 * @cfg {String} size xs | sm | md | lg
7150 * @cfg {Boolean} inverse false | true
7153 * Create a new Pagination
7154 * @param {Object} config The config object
7157 Roo.bootstrap.Pagination = function(config){
7158 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7161 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7167 getAutoCreate : function(){
7173 cfg.cls += ' inverse';
7179 cfg.cls += " " + this.cls;
7197 * @class Roo.bootstrap.PaginationItem
7198 * @extends Roo.bootstrap.Component
7199 * Bootstrap PaginationItem class
7200 * @cfg {String} html text
7201 * @cfg {String} href the link
7202 * @cfg {Boolean} preventDefault (true | false) default true
7203 * @cfg {Boolean} active (true | false) default false
7204 * @cfg {Boolean} disabled default false
7208 * Create a new PaginationItem
7209 * @param {Object} config The config object
7213 Roo.bootstrap.PaginationItem = function(config){
7214 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7219 * The raw click event for the entire grid.
7220 * @param {Roo.EventObject} e
7226 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7230 preventDefault: true,
7235 getAutoCreate : function(){
7241 href : this.href ? this.href : '#',
7242 html : this.html ? this.html : ''
7252 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7256 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7262 initEvents: function() {
7264 this.el.on('click', this.onClick, this);
7267 onClick : function(e)
7269 Roo.log('PaginationItem on click ');
7270 if(this.preventDefault){
7278 this.fireEvent('click', this, e);
7294 * @class Roo.bootstrap.Slider
7295 * @extends Roo.bootstrap.Component
7296 * Bootstrap Slider class
7299 * Create a new Slider
7300 * @param {Object} config The config object
7303 Roo.bootstrap.Slider = function(config){
7304 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7307 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7309 getAutoCreate : function(){
7313 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7317 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7329 * Ext JS Library 1.1.1
7330 * Copyright(c) 2006-2007, Ext JS, LLC.
7332 * Originally Released Under LGPL - original licence link has changed is not relivant.
7335 * <script type="text/javascript">
7338 * @extends Roo.dd.DDProxy
7339 * @class Roo.grid.SplitDragZone
7340 * Support for Column Header resizing
7342 * @param {Object} config
7345 // This is a support class used internally by the Grid components
7346 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7348 this.view = grid.getView();
7349 this.proxy = this.view.resizeProxy;
7350 Roo.grid.SplitDragZone.superclass.constructor.call(
7353 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7355 dragElId : Roo.id(this.proxy.dom),
7360 this.setHandleElId(Roo.id(hd));
7361 if (hd2 !== false) {
7362 this.setOuterHandleElId(Roo.id(hd2));
7365 this.scroll = false;
7367 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7368 fly: Roo.Element.fly,
7370 b4StartDrag : function(x, y){
7371 this.view.headersDisabled = true;
7372 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7373 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7375 this.proxy.setHeight(h);
7377 // for old system colWidth really stored the actual width?
7378 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7379 // which in reality did not work.. - it worked only for fixed sizes
7380 // for resizable we need to use actual sizes.
7381 var w = this.cm.getColumnWidth(this.cellIndex);
7382 if (!this.view.mainWrap) {
7384 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7389 // this was w-this.grid.minColumnWidth;
7390 // doesnt really make sense? - w = thie curren width or the rendered one?
7391 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7392 this.resetConstraints();
7393 this.setXConstraint(minw, 1000);
7394 this.setYConstraint(0, 0);
7395 this.minX = x - minw;
7396 this.maxX = x + 1000;
7398 if (!this.view.mainWrap) { // this is Bootstrap code..
7399 this.getDragEl().style.display='block';
7402 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7406 handleMouseDown : function(e){
7407 ev = Roo.EventObject.setEvent(e);
7408 var t = this.fly(ev.getTarget());
7409 if(t.hasClass("x-grid-split")){
7410 this.cellIndex = this.view.getCellIndex(t.dom);
7412 this.cm = this.grid.colModel;
7413 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7414 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7419 endDrag : function(e){
7420 this.view.headersDisabled = false;
7421 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7422 var diff = endX - this.startPos;
7424 var w = this.cm.getColumnWidth(this.cellIndex);
7425 if (!this.view.mainWrap) {
7428 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7431 autoOffset : function(){
7436 * Ext JS Library 1.1.1
7437 * Copyright(c) 2006-2007, Ext JS, LLC.
7439 * Originally Released Under LGPL - original licence link has changed is not relivant.
7442 * <script type="text/javascript">
7446 * @class Roo.grid.AbstractSelectionModel
7447 * @extends Roo.util.Observable
7449 * Abstract base class for grid SelectionModels. It provides the interface that should be
7450 * implemented by descendant classes. This class should not be directly instantiated.
7453 Roo.grid.AbstractSelectionModel = function(){
7454 this.locked = false;
7455 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7458 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7459 /** @ignore Called by the grid automatically. Do not call directly. */
7460 init : function(grid){
7466 * Locks the selections.
7473 * Unlocks the selections.
7475 unlock : function(){
7476 this.locked = false;
7480 * Returns true if the selections are locked.
7483 isLocked : function(){
7488 * Ext JS Library 1.1.1
7489 * Copyright(c) 2006-2007, Ext JS, LLC.
7491 * Originally Released Under LGPL - original licence link has changed is not relivant.
7494 * <script type="text/javascript">
7497 * @extends Roo.grid.AbstractSelectionModel
7498 * @class Roo.grid.RowSelectionModel
7499 * The default SelectionModel used by {@link Roo.grid.Grid}.
7500 * It supports multiple selections and keyboard selection/navigation.
7502 * @param {Object} config
7504 Roo.grid.RowSelectionModel = function(config){
7505 Roo.apply(this, config);
7506 this.selections = new Roo.util.MixedCollection(false, function(o){
7511 this.lastActive = false;
7515 * @event selectionchange
7516 * Fires when the selection changes
7517 * @param {SelectionModel} this
7519 "selectionchange" : true,
7521 * @event afterselectionchange
7522 * Fires after the selection changes (eg. by key press or clicking)
7523 * @param {SelectionModel} this
7525 "afterselectionchange" : true,
7527 * @event beforerowselect
7528 * Fires when a row is selected being selected, return false to cancel.
7529 * @param {SelectionModel} this
7530 * @param {Number} rowIndex The selected index
7531 * @param {Boolean} keepExisting False if other selections will be cleared
7533 "beforerowselect" : true,
7536 * Fires when a row is selected.
7537 * @param {SelectionModel} this
7538 * @param {Number} rowIndex The selected index
7539 * @param {Roo.data.Record} r The record
7543 * @event rowdeselect
7544 * Fires when a row is deselected.
7545 * @param {SelectionModel} this
7546 * @param {Number} rowIndex The selected index
7548 "rowdeselect" : true
7550 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7551 this.locked = false;
7554 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7556 * @cfg {Boolean} singleSelect
7557 * True to allow selection of only one row at a time (defaults to false)
7559 singleSelect : false,
7562 initEvents : function(){
7564 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7565 this.grid.on("mousedown", this.handleMouseDown, this);
7566 }else{ // allow click to work like normal
7567 this.grid.on("rowclick", this.handleDragableRowClick, this);
7569 // bootstrap does not have a view..
7570 var view = this.grid.view ? this.grid.view : this.grid;
7571 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7574 this.selectPrevious(e.shiftKey);
7575 }else if(this.last !== false && this.lastActive !== false){
7576 var last = this.last;
7577 this.selectRange(this.last, this.lastActive-1);
7578 view.focusRow(this.lastActive);
7583 this.selectFirstRow();
7585 this.fireEvent("afterselectionchange", this);
7587 "down" : function(e){
7589 this.selectNext(e.shiftKey);
7590 }else if(this.last !== false && this.lastActive !== false){
7591 var last = this.last;
7592 this.selectRange(this.last, this.lastActive+1);
7593 view.focusRow(this.lastActive);
7598 this.selectFirstRow();
7600 this.fireEvent("afterselectionchange", this);
7606 view.on("refresh", this.onRefresh, this);
7607 view.on("rowupdated", this.onRowUpdated, this);
7608 view.on("rowremoved", this.onRemove, this);
7612 onRefresh : function(){
7613 var ds = this.grid.ds, i, v = this.grid.view;
7614 var s = this.selections;
7616 if((i = ds.indexOfId(r.id)) != -1){
7618 s.add(ds.getAt(i)); // updating the selection relate data
7626 onRemove : function(v, index, r){
7627 this.selections.remove(r);
7631 onRowUpdated : function(v, index, r){
7632 if(this.isSelected(r)){
7633 v.onRowSelect(index);
7639 * @param {Array} records The records to select
7640 * @param {Boolean} keepExisting (optional) True to keep existing selections
7642 selectRecords : function(records, keepExisting){
7644 this.clearSelections();
7646 var ds = this.grid.ds;
7647 for(var i = 0, len = records.length; i < len; i++){
7648 this.selectRow(ds.indexOf(records[i]), true);
7653 * Gets the number of selected rows.
7656 getCount : function(){
7657 return this.selections.length;
7661 * Selects the first row in the grid.
7663 selectFirstRow : function(){
7668 * Select the last row.
7669 * @param {Boolean} keepExisting (optional) True to keep existing selections
7671 selectLastRow : function(keepExisting){
7672 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7676 * Selects the row immediately following the last selected row.
7677 * @param {Boolean} keepExisting (optional) True to keep existing selections
7679 selectNext : function(keepExisting){
7680 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7681 this.selectRow(this.last+1, keepExisting);
7682 var view = this.grid.view ? this.grid.view : this.grid;
7683 view.focusRow(this.last);
7688 * Selects the row that precedes the last selected row.
7689 * @param {Boolean} keepExisting (optional) True to keep existing selections
7691 selectPrevious : function(keepExisting){
7693 this.selectRow(this.last-1, keepExisting);
7694 var view = this.grid.view ? this.grid.view : this.grid;
7695 view.focusRow(this.last);
7700 * Returns the selected records
7701 * @return {Array} Array of selected records
7703 getSelections : function(){
7704 return [].concat(this.selections.items);
7708 * Returns the first selected record.
7711 getSelected : function(){
7712 return this.selections.itemAt(0);
7717 * Clears all selections.
7719 clearSelections : function(fast){
7724 var ds = this.grid.ds;
7725 var s = this.selections;
7727 this.deselectRow(ds.indexOfId(r.id));
7731 this.selections.clear();
7740 selectAll : function(){
7744 this.selections.clear();
7745 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7746 this.selectRow(i, true);
7751 * Returns True if there is a selection.
7754 hasSelection : function(){
7755 return this.selections.length > 0;
7759 * Returns True if the specified row is selected.
7760 * @param {Number/Record} record The record or index of the record to check
7763 isSelected : function(index){
7764 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7765 return (r && this.selections.key(r.id) ? true : false);
7769 * Returns True if the specified record id is selected.
7770 * @param {String} id The id of record to check
7773 isIdSelected : function(id){
7774 return (this.selections.key(id) ? true : false);
7778 handleMouseDown : function(e, t)
7780 var view = this.grid.view ? this.grid.view : this.grid;
7782 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7785 if(e.shiftKey && this.last !== false){
7786 var last = this.last;
7787 this.selectRange(last, rowIndex, e.ctrlKey);
7788 this.last = last; // reset the last
7789 view.focusRow(rowIndex);
7791 var isSelected = this.isSelected(rowIndex);
7792 if(e.button !== 0 && isSelected){
7793 view.focusRow(rowIndex);
7794 }else if(e.ctrlKey && isSelected){
7795 this.deselectRow(rowIndex);
7796 }else if(!isSelected){
7797 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7798 view.focusRow(rowIndex);
7801 this.fireEvent("afterselectionchange", this);
7804 handleDragableRowClick : function(grid, rowIndex, e)
7806 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7807 this.selectRow(rowIndex, false);
7808 var view = this.grid.view ? this.grid.view : this.grid;
7809 view.focusRow(rowIndex);
7810 this.fireEvent("afterselectionchange", this);
7815 * Selects multiple rows.
7816 * @param {Array} rows Array of the indexes of the row to select
7817 * @param {Boolean} keepExisting (optional) True to keep existing selections
7819 selectRows : function(rows, keepExisting){
7821 this.clearSelections();
7823 for(var i = 0, len = rows.length; i < len; i++){
7824 this.selectRow(rows[i], true);
7829 * Selects a range of rows. All rows in between startRow and endRow are also selected.
7830 * @param {Number} startRow The index of the first row in the range
7831 * @param {Number} endRow The index of the last row in the range
7832 * @param {Boolean} keepExisting (optional) True to retain existing selections
7834 selectRange : function(startRow, endRow, keepExisting){
7839 this.clearSelections();
7841 if(startRow <= endRow){
7842 for(var i = startRow; i <= endRow; i++){
7843 this.selectRow(i, true);
7846 for(var i = startRow; i >= endRow; i--){
7847 this.selectRow(i, true);
7853 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7854 * @param {Number} startRow The index of the first row in the range
7855 * @param {Number} endRow The index of the last row in the range
7857 deselectRange : function(startRow, endRow, preventViewNotify){
7861 for(var i = startRow; i <= endRow; i++){
7862 this.deselectRow(i, preventViewNotify);
7868 * @param {Number} row The index of the row to select
7869 * @param {Boolean} keepExisting (optional) True to keep existing selections
7871 selectRow : function(index, keepExisting, preventViewNotify){
7872 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7875 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7876 if(!keepExisting || this.singleSelect){
7877 this.clearSelections();
7879 var r = this.grid.ds.getAt(index);
7880 this.selections.add(r);
7881 this.last = this.lastActive = index;
7882 if(!preventViewNotify){
7883 var view = this.grid.view ? this.grid.view : this.grid;
7884 view.onRowSelect(index);
7886 this.fireEvent("rowselect", this, index, r);
7887 this.fireEvent("selectionchange", this);
7893 * @param {Number} row The index of the row to deselect
7895 deselectRow : function(index, preventViewNotify){
7899 if(this.last == index){
7902 if(this.lastActive == index){
7903 this.lastActive = false;
7905 var r = this.grid.ds.getAt(index);
7906 this.selections.remove(r);
7907 if(!preventViewNotify){
7908 var view = this.grid.view ? this.grid.view : this.grid;
7909 view.onRowDeselect(index);
7911 this.fireEvent("rowdeselect", this, index);
7912 this.fireEvent("selectionchange", this);
7916 restoreLast : function(){
7918 this.last = this._last;
7923 acceptsNav : function(row, col, cm){
7924 return !cm.isHidden(col) && cm.isCellEditable(col, row);
7928 onEditorKey : function(field, e){
7929 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7934 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7936 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7938 }else if(k == e.ENTER && !e.ctrlKey){
7942 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7944 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7946 }else if(k == e.ESC){
7950 g.startEditing(newCell[0], newCell[1]);
7955 * Ext JS Library 1.1.1
7956 * Copyright(c) 2006-2007, Ext JS, LLC.
7958 * Originally Released Under LGPL - original licence link has changed is not relivant.
7961 * <script type="text/javascript">
7966 * @class Roo.grid.ColumnModel
7967 * @extends Roo.util.Observable
7968 * This is the default implementation of a ColumnModel used by the Grid. It defines
7969 * the columns in the grid.
7972 var colModel = new Roo.grid.ColumnModel([
7973 {header: "Ticker", width: 60, sortable: true, locked: true},
7974 {header: "Company Name", width: 150, sortable: true},
7975 {header: "Market Cap.", width: 100, sortable: true},
7976 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7977 {header: "Employees", width: 100, sortable: true, resizable: false}
7982 * The config options listed for this class are options which may appear in each
7983 * individual column definition.
7984 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7986 * @param {Object} config An Array of column config objects. See this class's
7987 * config objects for details.
7989 Roo.grid.ColumnModel = function(config){
7991 * The config passed into the constructor
7993 this.config = []; //config;
7996 // if no id, create one
7997 // if the column does not have a dataIndex mapping,
7998 // map it to the order it is in the config
7999 for(var i = 0, len = config.length; i < len; i++){
8000 this.addColumn(config[i]);
8005 * The width of columns which have no width specified (defaults to 100)
8008 this.defaultWidth = 100;
8011 * Default sortable of columns which have no sortable specified (defaults to false)
8014 this.defaultSortable = false;
8018 * @event widthchange
8019 * Fires when the width of a column changes.
8020 * @param {ColumnModel} this
8021 * @param {Number} columnIndex The column index
8022 * @param {Number} newWidth The new width
8024 "widthchange": true,
8026 * @event headerchange
8027 * Fires when the text of a header changes.
8028 * @param {ColumnModel} this
8029 * @param {Number} columnIndex The column index
8030 * @param {Number} newText The new header text
8032 "headerchange": true,
8034 * @event hiddenchange
8035 * Fires when a column is hidden or "unhidden".
8036 * @param {ColumnModel} this
8037 * @param {Number} columnIndex The column index
8038 * @param {Boolean} hidden true if hidden, false otherwise
8040 "hiddenchange": true,
8042 * @event columnmoved
8043 * Fires when a column is moved.
8044 * @param {ColumnModel} this
8045 * @param {Number} oldIndex
8046 * @param {Number} newIndex
8048 "columnmoved" : true,
8050 * @event columlockchange
8051 * Fires when a column's locked state is changed
8052 * @param {ColumnModel} this
8053 * @param {Number} colIndex
8054 * @param {Boolean} locked true if locked
8056 "columnlockchange" : true
8058 Roo.grid.ColumnModel.superclass.constructor.call(this);
8060 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8062 * @cfg {String} header The header text to display in the Grid view.
8065 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8068 * @cfg {String} smHeader Header at Bootsrap Small width
8071 * @cfg {String} mdHeader Header at Bootsrap Medium width
8074 * @cfg {String} lgHeader Header at Bootsrap Large width
8077 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8080 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8081 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8082 * specified, the column's index is used as an index into the Record's data Array.
8085 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8086 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8089 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8090 * Defaults to the value of the {@link #defaultSortable} property.
8091 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8094 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
8097 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
8100 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8103 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8106 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8107 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8108 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8109 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8112 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
8115 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
8118 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
8121 * @cfg {String} cursor (Optional)
8124 * @cfg {String} tooltip (Optional)
8127 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8130 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8133 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8136 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8139 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8142 * Returns the id of the column at the specified index.
8143 * @param {Number} index The column index
8144 * @return {String} the id
8146 getColumnId : function(index){
8147 return this.config[index].id;
8151 * Returns the column for a specified id.
8152 * @param {String} id The column id
8153 * @return {Object} the column
8155 getColumnById : function(id){
8156 return this.lookup[id];
8161 * Returns the column Object for a specified dataIndex.
8162 * @param {String} dataIndex The column dataIndex
8163 * @return {Object|Boolean} the column or false if not found
8165 getColumnByDataIndex: function(dataIndex){
8166 var index = this.findColumnIndex(dataIndex);
8167 return index > -1 ? this.config[index] : false;
8171 * Returns the index for a specified column id.
8172 * @param {String} id The column id
8173 * @return {Number} the index, or -1 if not found
8175 getIndexById : function(id){
8176 for(var i = 0, len = this.config.length; i < len; i++){
8177 if(this.config[i].id == id){
8185 * Returns the index for a specified column dataIndex.
8186 * @param {String} dataIndex The column dataIndex
8187 * @return {Number} the index, or -1 if not found
8190 findColumnIndex : function(dataIndex){
8191 for(var i = 0, len = this.config.length; i < len; i++){
8192 if(this.config[i].dataIndex == dataIndex){
8200 moveColumn : function(oldIndex, newIndex){
8201 var c = this.config[oldIndex];
8202 this.config.splice(oldIndex, 1);
8203 this.config.splice(newIndex, 0, c);
8204 this.dataMap = null;
8205 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8208 isLocked : function(colIndex){
8209 return this.config[colIndex].locked === true;
8212 setLocked : function(colIndex, value, suppressEvent){
8213 if(this.isLocked(colIndex) == value){
8216 this.config[colIndex].locked = value;
8218 this.fireEvent("columnlockchange", this, colIndex, value);
8222 getTotalLockedWidth : function(){
8224 for(var i = 0; i < this.config.length; i++){
8225 if(this.isLocked(i) && !this.isHidden(i)){
8226 this.totalWidth += this.getColumnWidth(i);
8232 getLockedCount : function(){
8233 for(var i = 0, len = this.config.length; i < len; i++){
8234 if(!this.isLocked(i)){
8239 return this.config.length;
8243 * Returns the number of columns.
8246 getColumnCount : function(visibleOnly){
8247 if(visibleOnly === true){
8249 for(var i = 0, len = this.config.length; i < len; i++){
8250 if(!this.isHidden(i)){
8256 return this.config.length;
8260 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8261 * @param {Function} fn
8262 * @param {Object} scope (optional)
8263 * @return {Array} result
8265 getColumnsBy : function(fn, scope){
8267 for(var i = 0, len = this.config.length; i < len; i++){
8268 var c = this.config[i];
8269 if(fn.call(scope||this, c, i) === true){
8277 * Returns true if the specified column is sortable.
8278 * @param {Number} col The column index
8281 isSortable : function(col){
8282 if(typeof this.config[col].sortable == "undefined"){
8283 return this.defaultSortable;
8285 return this.config[col].sortable;
8289 * Returns the rendering (formatting) function defined for the column.
8290 * @param {Number} col The column index.
8291 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8293 getRenderer : function(col){
8294 if(!this.config[col].renderer){
8295 return Roo.grid.ColumnModel.defaultRenderer;
8297 return this.config[col].renderer;
8301 * Sets the rendering (formatting) function for a column.
8302 * @param {Number} col The column index
8303 * @param {Function} fn The function to use to process the cell's raw data
8304 * to return HTML markup for the grid view. The render function is called with
8305 * the following parameters:<ul>
8306 * <li>Data value.</li>
8307 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8308 * <li>css A CSS style string to apply to the table cell.</li>
8309 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8310 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8311 * <li>Row index</li>
8312 * <li>Column index</li>
8313 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8315 setRenderer : function(col, fn){
8316 this.config[col].renderer = fn;
8320 * Returns the width for the specified column.
8321 * @param {Number} col The column index
8322 * @param (optional) {String} gridSize bootstrap width size.
8325 getColumnWidth : function(col, gridSize)
8327 var cfg = this.config[col];
8329 if (typeof(gridSize) == 'undefined') {
8330 return cfg.width * 1 || this.defaultWidth;
8332 if (gridSize === false) { // if we set it..
8333 return cfg.width || false;
8335 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8337 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8338 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8341 return cfg[ sizes[i] ];
8348 * Sets the width for a column.
8349 * @param {Number} col The column index
8350 * @param {Number} width The new width
8352 setColumnWidth : function(col, width, suppressEvent){
8353 this.config[col].width = width;
8354 this.totalWidth = null;
8356 this.fireEvent("widthchange", this, col, width);
8361 * Returns the total width of all columns.
8362 * @param {Boolean} includeHidden True to include hidden column widths
8365 getTotalWidth : function(includeHidden){
8366 if(!this.totalWidth){
8367 this.totalWidth = 0;
8368 for(var i = 0, len = this.config.length; i < len; i++){
8369 if(includeHidden || !this.isHidden(i)){
8370 this.totalWidth += this.getColumnWidth(i);
8374 return this.totalWidth;
8378 * Returns the header for the specified column.
8379 * @param {Number} col The column index
8382 getColumnHeader : function(col){
8383 return this.config[col].header;
8387 * Sets the header for a column.
8388 * @param {Number} col The column index
8389 * @param {String} header The new header
8391 setColumnHeader : function(col, header){
8392 this.config[col].header = header;
8393 this.fireEvent("headerchange", this, col, header);
8397 * Returns the tooltip for the specified column.
8398 * @param {Number} col The column index
8401 getColumnTooltip : function(col){
8402 return this.config[col].tooltip;
8405 * Sets the tooltip for a column.
8406 * @param {Number} col The column index
8407 * @param {String} tooltip The new tooltip
8409 setColumnTooltip : function(col, tooltip){
8410 this.config[col].tooltip = tooltip;
8414 * Returns the dataIndex for the specified column.
8415 * @param {Number} col The column index
8418 getDataIndex : function(col){
8419 return this.config[col].dataIndex;
8423 * Sets the dataIndex for a column.
8424 * @param {Number} col The column index
8425 * @param {Number} dataIndex The new dataIndex
8427 setDataIndex : function(col, dataIndex){
8428 this.config[col].dataIndex = dataIndex;
8434 * Returns true if the cell is editable.
8435 * @param {Number} colIndex The column index
8436 * @param {Number} rowIndex The row index - this is nto actually used..?
8439 isCellEditable : function(colIndex, rowIndex){
8440 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8444 * Returns the editor defined for the cell/column.
8445 * return false or null to disable editing.
8446 * @param {Number} colIndex The column index
8447 * @param {Number} rowIndex The row index
8450 getCellEditor : function(colIndex, rowIndex){
8451 return this.config[colIndex].editor;
8455 * Sets if a column is editable.
8456 * @param {Number} col The column index
8457 * @param {Boolean} editable True if the column is editable
8459 setEditable : function(col, editable){
8460 this.config[col].editable = editable;
8465 * Returns true if the column is hidden.
8466 * @param {Number} colIndex The column index
8469 isHidden : function(colIndex){
8470 return this.config[colIndex].hidden;
8475 * Returns true if the column width cannot be changed
8477 isFixed : function(colIndex){
8478 return this.config[colIndex].fixed;
8482 * Returns true if the column can be resized
8485 isResizable : function(colIndex){
8486 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8489 * Sets if a column is hidden.
8490 * @param {Number} colIndex The column index
8491 * @param {Boolean} hidden True if the column is hidden
8493 setHidden : function(colIndex, hidden){
8494 this.config[colIndex].hidden = hidden;
8495 this.totalWidth = null;
8496 this.fireEvent("hiddenchange", this, colIndex, hidden);
8500 * Sets the editor for a column.
8501 * @param {Number} col The column index
8502 * @param {Object} editor The editor object
8504 setEditor : function(col, editor){
8505 this.config[col].editor = editor;
8508 * Add a column (experimental...) - defaults to adding to the end..
8509 * @param {Object} config
8511 addColumn : function(c)
8514 var i = this.config.length;
8517 if(typeof c.dataIndex == "undefined"){
8520 if(typeof c.renderer == "string"){
8521 c.renderer = Roo.util.Format[c.renderer];
8523 if(typeof c.id == "undefined"){
8526 if(c.editor && c.editor.xtype){
8527 c.editor = Roo.factory(c.editor, Roo.grid);
8529 if(c.editor && c.editor.isFormField){
8530 c.editor = new Roo.grid.GridEditor(c.editor);
8532 this.lookup[c.id] = c;
8537 Roo.grid.ColumnModel.defaultRenderer = function(value)
8539 if(typeof value == "object") {
8542 if(typeof value == "string" && value.length < 1){
8546 return String.format("{0}", value);
8549 // Alias for backwards compatibility
8550 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8553 * Ext JS Library 1.1.1
8554 * Copyright(c) 2006-2007, Ext JS, LLC.
8556 * Originally Released Under LGPL - original licence link has changed is not relivant.
8559 * <script type="text/javascript">
8563 * @class Roo.LoadMask
8564 * A simple utility class for generically masking elements while loading data. If the element being masked has
8565 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8566 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8567 * element's UpdateManager load indicator and will be destroyed after the initial load.
8569 * Create a new LoadMask
8570 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8571 * @param {Object} config The config object
8573 Roo.LoadMask = function(el, config){
8574 this.el = Roo.get(el);
8575 Roo.apply(this, config);
8577 this.store.on('beforeload', this.onBeforeLoad, this);
8578 this.store.on('load', this.onLoad, this);
8579 this.store.on('loadexception', this.onLoadException, this);
8580 this.removeMask = false;
8582 var um = this.el.getUpdateManager();
8583 um.showLoadIndicator = false; // disable the default indicator
8584 um.on('beforeupdate', this.onBeforeLoad, this);
8585 um.on('update', this.onLoad, this);
8586 um.on('failure', this.onLoad, this);
8587 this.removeMask = true;
8591 Roo.LoadMask.prototype = {
8593 * @cfg {Boolean} removeMask
8594 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8595 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
8600 * The text to display in a centered loading message box (defaults to 'Loading...')
8604 * @cfg {String} msgCls
8605 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8607 msgCls : 'x-mask-loading',
8610 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8616 * Disables the mask to prevent it from being displayed
8618 disable : function(){
8619 this.disabled = true;
8623 * Enables the mask so that it can be displayed
8625 enable : function(){
8626 this.disabled = false;
8629 onLoadException : function()
8633 if (typeof(arguments[3]) != 'undefined') {
8634 Roo.MessageBox.alert("Error loading",arguments[3]);
8638 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8639 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8646 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8651 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8655 onBeforeLoad : function(){
8657 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8662 destroy : function(){
8664 this.store.un('beforeload', this.onBeforeLoad, this);
8665 this.store.un('load', this.onLoad, this);
8666 this.store.un('loadexception', this.onLoadException, this);
8668 var um = this.el.getUpdateManager();
8669 um.un('beforeupdate', this.onBeforeLoad, this);
8670 um.un('update', this.onLoad, this);
8671 um.un('failure', this.onLoad, this);
8675 * @class Roo.bootstrap.Table
8677 * @extends Roo.bootstrap.Component
8678 * @children Roo.bootstrap.TableBody
8679 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
8680 * Similar to Roo.grid.Grid
8682 var table = Roo.factory({
8684 xns : Roo.bootstrap,
8685 autoSizeColumns: true,
8692 sortInfo : { direction : 'ASC', field: 'name' },
8694 xtype : 'HttpProxy',
8697 url : 'https://example.com/some.data.url.json'
8700 xtype : 'JsonReader',
8702 fields : [ 'id', 'name', whatever' ],
8709 xtype : 'ColumnModel',
8713 dataIndex : 'is_in_group',
8716 renderer : function(v, x , r) {
8718 return String.format("{0}", v)
8724 xtype : 'RowSelectionModel',
8725 xns : Roo.bootstrap.Table
8726 // you can add listeners to catch selection change here....
8732 grid.render(Roo.get("some-div"));
8735 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
8740 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
8741 * @cfg {Roo.data.Store} store The data store to use
8742 * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
8744 * @cfg {String} cls table class
8747 * @cfg {boolean} striped Should the rows be alternative striped
8748 * @cfg {boolean} bordered Add borders to the table
8749 * @cfg {boolean} hover Add hover highlighting
8750 * @cfg {boolean} condensed Format condensed
8751 * @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,
8752 * also adds table-responsive (see bootstrap docs for details)
8753 * @cfg {Boolean} loadMask (true|false) default false
8754 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8755 * @cfg {Boolean} headerShow (true|false) generate thead, default true
8756 * @cfg {Boolean} rowSelection (true|false) default false
8757 * @cfg {Boolean} cellSelection (true|false) default false
8758 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8759 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
8760 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8761 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8762 * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8763 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
8766 * Create a new Table
8767 * @param {Object} config The config object
8770 Roo.bootstrap.Table = function(config)
8772 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8775 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8776 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8777 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8778 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8780 this.view = this; // compat with grid.
8782 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8784 this.sm.grid = this;
8785 this.selModel = Roo.factory(this.sm, Roo.grid);
8786 this.sm = this.selModel;
8787 this.sm.xmodule = this.xmodule || false;
8790 if (this.cm && typeof(this.cm.config) == 'undefined') {
8791 this.colModel = new Roo.grid.ColumnModel(this.cm);
8792 this.cm = this.colModel;
8793 this.cm.xmodule = this.xmodule || false;
8796 this.store= Roo.factory(this.store, Roo.data);
8797 this.ds = this.store;
8798 this.ds.xmodule = this.xmodule || false;
8801 if (this.footer && this.store) {
8802 this.footer.dataSource = this.ds;
8803 this.footer = Roo.factory(this.footer);
8810 * Fires when a cell is clicked
8811 * @param {Roo.bootstrap.Table} this
8812 * @param {Roo.Element} el
8813 * @param {Number} rowIndex
8814 * @param {Number} columnIndex
8815 * @param {Roo.EventObject} e
8819 * @event celldblclick
8820 * Fires when a cell is double clicked
8821 * @param {Roo.bootstrap.Table} this
8822 * @param {Roo.Element} el
8823 * @param {Number} rowIndex
8824 * @param {Number} columnIndex
8825 * @param {Roo.EventObject} e
8827 "celldblclick" : true,
8830 * Fires when a row is clicked
8831 * @param {Roo.bootstrap.Table} this
8832 * @param {Roo.Element} el
8833 * @param {Number} rowIndex
8834 * @param {Roo.EventObject} e
8838 * @event rowdblclick
8839 * Fires when a row is double clicked
8840 * @param {Roo.bootstrap.Table} this
8841 * @param {Roo.Element} el
8842 * @param {Number} rowIndex
8843 * @param {Roo.EventObject} e
8845 "rowdblclick" : true,
8848 * Fires when a mouseover occur
8849 * @param {Roo.bootstrap.Table} this
8850 * @param {Roo.Element} el
8851 * @param {Number} rowIndex
8852 * @param {Number} columnIndex
8853 * @param {Roo.EventObject} e
8858 * Fires when a mouseout occur
8859 * @param {Roo.bootstrap.Table} this
8860 * @param {Roo.Element} el
8861 * @param {Number} rowIndex
8862 * @param {Number} columnIndex
8863 * @param {Roo.EventObject} e
8868 * Fires when a row is rendered, so you can change add a style to it.
8869 * @param {Roo.bootstrap.Table} this
8870 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8874 * @event rowsrendered
8875 * Fires when all the rows have been rendered
8876 * @param {Roo.bootstrap.Table} this
8878 'rowsrendered' : true,
8880 * @event contextmenu
8881 * The raw contextmenu event for the entire grid.
8882 * @param {Roo.EventObject} e
8884 "contextmenu" : true,
8886 * @event rowcontextmenu
8887 * Fires when a row is right clicked
8888 * @param {Roo.bootstrap.Table} this
8889 * @param {Number} rowIndex
8890 * @param {Roo.EventObject} e
8892 "rowcontextmenu" : true,
8894 * @event cellcontextmenu
8895 * Fires when a cell is right clicked
8896 * @param {Roo.bootstrap.Table} this
8897 * @param {Number} rowIndex
8898 * @param {Number} cellIndex
8899 * @param {Roo.EventObject} e
8901 "cellcontextmenu" : true,
8903 * @event headercontextmenu
8904 * Fires when a header is right clicked
8905 * @param {Roo.bootstrap.Table} this
8906 * @param {Number} columnIndex
8907 * @param {Roo.EventObject} e
8909 "headercontextmenu" : true,
8912 * The raw mousedown event for the entire grid.
8913 * @param {Roo.EventObject} e
8920 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8936 enableColumnResize: true,
8938 rowSelection : false,
8939 cellSelection : false,
8942 minColumnWidth : 50,
8944 // Roo.Element - the tbody
8945 bodyEl: false, // <tbody> Roo.Element - thead element
8946 headEl: false, // <thead> Roo.Element - thead element
8947 resizeProxy : false, // proxy element for dragging?
8951 container: false, // used by gridpanel...
8957 auto_hide_footer : false,
8959 view: false, // actually points to this..
8961 getAutoCreate : function()
8963 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8970 // this get's auto added by panel.Grid
8971 if (this.scrollBody) {
8972 cfg.cls += ' table-body-fixed';
8975 cfg.cls += ' table-striped';
8979 cfg.cls += ' table-hover';
8981 if (this.bordered) {
8982 cfg.cls += ' table-bordered';
8984 if (this.condensed) {
8985 cfg.cls += ' table-condensed';
8988 if (this.responsive) {
8989 cfg.cls += ' table-responsive';
8993 cfg.cls+= ' ' +this.cls;
8999 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9002 if(this.store || this.cm){
9003 if(this.headerShow){
9004 cfg.cn.push(this.renderHeader());
9007 cfg.cn.push(this.renderBody());
9009 if(this.footerShow){
9010 cfg.cn.push(this.renderFooter());
9012 // where does this come from?
9013 //cfg.cls+= ' TableGrid';
9016 return { cn : [ cfg ] };
9019 initEvents : function()
9021 if(!this.store || !this.cm){
9024 if (this.selModel) {
9025 this.selModel.initEvents();
9029 //Roo.log('initEvents with ds!!!!');
9031 this.bodyEl = this.el.select('tbody', true).first();
9032 this.headEl = this.el.select('thead', true).first();
9033 this.mainFoot = this.el.select('tfoot', true).first();
9038 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9039 e.on('click', this.sort, this);
9043 // why is this done????? = it breaks dialogs??
9044 //this.parent().el.setStyle('position', 'relative');
9048 this.footer.parentId = this.id;
9049 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9052 this.el.select('tfoot tr td').first().addClass('hide');
9057 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9060 this.store.on('load', this.onLoad, this);
9061 this.store.on('beforeload', this.onBeforeLoad, this);
9062 this.store.on('update', this.onUpdate, this);
9063 this.store.on('add', this.onAdd, this);
9064 this.store.on("clear", this.clear, this);
9066 this.el.on("contextmenu", this.onContextMenu, this);
9069 this.cm.on("headerchange", this.onHeaderChange, this);
9070 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9072 //?? does bodyEl get replaced on render?
9073 this.bodyEl.on("click", this.onClick, this);
9074 this.bodyEl.on("dblclick", this.onDblClick, this);
9075 this.bodyEl.on('scroll', this.onBodyScroll, this);
9077 // guessing mainbody will work - this relays usually caught by selmodel at present.
9078 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9081 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9084 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9085 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9090 // Compatibility with grid - we implement all the view features at present.
9091 getView : function()
9096 initCSS : function()
9100 var cm = this.cm, styles = [];
9101 this.CSS.removeStyleSheet(this.id + '-cssrules');
9102 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9103 // we can honour xs/sm/md/xl as widths...
9104 // we first have to decide what widht we are currently at...
9105 var sz = Roo.getGridSize();
9109 var cols = []; // visable cols.
9111 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9112 var w = cm.getColumnWidth(i, false);
9114 cols.push( { rel : false, abs : 0 });
9118 cols.push( { rel : false, abs : w });
9120 last = i; // not really..
9123 var w = cm.getColumnWidth(i, sz);
9128 cols.push( { rel : w, abs : false });
9131 var avail = this.bodyEl.dom.clientWidth - total_abs;
9133 var unitWidth = Math.floor(avail / total);
9134 var rem = avail - (unitWidth * total);
9136 var hidden, width, pos = 0 , splithide , left;
9137 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9139 hidden = 'display:none;';
9141 width = 'width:0px;';
9143 if(!cm.isHidden(i)){
9147 // we can honour xs/sm/md/xl ?
9148 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9150 hidden = 'display:none;';
9152 // width should return a small number...
9154 w+=rem; // add the remaining with..
9157 left = "left:" + (pos -4) + "px;";
9158 width = "width:" + w+ "px;";
9161 if (this.responsive) {
9164 hidden = cm.isHidden(i) ? 'display:none;' : '';
9165 splithide = 'display: none;';
9168 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9171 splithide = 'display:none;';
9174 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9175 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9180 //Roo.log(styles.join(''));
9181 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9187 onContextMenu : function(e, t)
9189 this.processEvent("contextmenu", e);
9192 processEvent : function(name, e)
9194 if (name != 'touchstart' ) {
9195 this.fireEvent(name, e);
9198 var t = e.getTarget();
9200 var cell = Roo.get(t);
9206 if(cell.findParent('tfoot', false, true)){
9210 if(cell.findParent('thead', false, true)){
9212 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9213 cell = Roo.get(t).findParent('th', false, true);
9215 Roo.log("failed to find th in thead?");
9216 Roo.log(e.getTarget());
9221 var cellIndex = cell.dom.cellIndex;
9223 var ename = name == 'touchstart' ? 'click' : name;
9224 this.fireEvent("header" + ename, this, cellIndex, e);
9229 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9230 cell = Roo.get(t).findParent('td', false, true);
9232 Roo.log("failed to find th in tbody?");
9233 Roo.log(e.getTarget());
9238 var row = cell.findParent('tr', false, true);
9239 var cellIndex = cell.dom.cellIndex;
9240 var rowIndex = row.dom.rowIndex - 1;
9244 this.fireEvent("row" + name, this, rowIndex, e);
9248 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9254 onMouseover : function(e, el)
9256 var cell = Roo.get(el);
9262 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9263 cell = cell.findParent('td', false, true);
9266 var row = cell.findParent('tr', false, true);
9267 var cellIndex = cell.dom.cellIndex;
9268 var rowIndex = row.dom.rowIndex - 1; // start from 0
9270 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9274 onMouseout : function(e, el)
9276 var cell = Roo.get(el);
9282 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9283 cell = cell.findParent('td', false, true);
9286 var row = cell.findParent('tr', false, true);
9287 var cellIndex = cell.dom.cellIndex;
9288 var rowIndex = row.dom.rowIndex - 1; // start from 0
9290 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9294 onClick : function(e, el)
9296 var cell = Roo.get(el);
9298 if(!cell || (!this.cellSelection && !this.rowSelection)){
9302 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9303 cell = cell.findParent('td', false, true);
9306 if(!cell || typeof(cell) == 'undefined'){
9310 var row = cell.findParent('tr', false, true);
9312 if(!row || typeof(row) == 'undefined'){
9316 var cellIndex = cell.dom.cellIndex;
9317 var rowIndex = this.getRowIndex(row);
9319 // why??? - should these not be based on SelectionModel?
9320 //if(this.cellSelection){
9321 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9324 //if(this.rowSelection){
9325 this.fireEvent('rowclick', this, row, rowIndex, e);
9330 onDblClick : function(e,el)
9332 var cell = Roo.get(el);
9334 if(!cell || (!this.cellSelection && !this.rowSelection)){
9338 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9339 cell = cell.findParent('td', false, true);
9342 if(!cell || typeof(cell) == 'undefined'){
9346 var row = cell.findParent('tr', false, true);
9348 if(!row || typeof(row) == 'undefined'){
9352 var cellIndex = cell.dom.cellIndex;
9353 var rowIndex = this.getRowIndex(row);
9355 if(this.cellSelection){
9356 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9359 if(this.rowSelection){
9360 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9363 findRowIndex : function(el)
9365 var cell = Roo.get(el);
9369 var row = cell.findParent('tr', false, true);
9371 if(!row || typeof(row) == 'undefined'){
9374 return this.getRowIndex(row);
9376 sort : function(e,el)
9378 var col = Roo.get(el);
9380 if(!col.hasClass('sortable')){
9384 var sort = col.attr('sort');
9387 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9391 this.store.sortInfo = {field : sort, direction : dir};
9394 Roo.log("calling footer first");
9395 this.footer.onClick('first');
9398 this.store.load({ params : { start : 0 } });
9402 renderHeader : function()
9410 this.totalWidth = 0;
9412 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9414 var config = cm.config[i];
9418 cls : 'x-hcol-' + i,
9421 html: cm.getColumnHeader(i)
9424 var tooltip = cm.getColumnTooltip(i);
9426 c.tooltip = tooltip;
9432 if(typeof(config.sortable) != 'undefined' && config.sortable){
9433 c.cls += ' sortable';
9434 c.html = '<i class="fa"></i>' + c.html;
9437 // could use BS4 hidden-..-down
9439 if(typeof(config.lgHeader) != 'undefined'){
9440 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9443 if(typeof(config.mdHeader) != 'undefined'){
9444 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9447 if(typeof(config.smHeader) != 'undefined'){
9448 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9451 if(typeof(config.xsHeader) != 'undefined'){
9452 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9459 if(typeof(config.tooltip) != 'undefined'){
9460 c.tooltip = config.tooltip;
9463 if(typeof(config.colspan) != 'undefined'){
9464 c.colspan = config.colspan;
9467 // hidden is handled by CSS now
9469 if(typeof(config.dataIndex) != 'undefined'){
9470 c.sort = config.dataIndex;
9475 if(typeof(config.align) != 'undefined' && config.align.length){
9476 c.style += ' text-align:' + config.align + ';';
9479 /* width is done in CSS
9480 *if(typeof(config.width) != 'undefined'){
9481 c.style += ' width:' + config.width + 'px;';
9482 this.totalWidth += config.width;
9484 this.totalWidth += 100; // assume minimum of 100 per column?
9488 if(typeof(config.cls) != 'undefined'){
9489 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9491 // this is the bit that doesnt reall work at all...
9493 if (this.responsive) {
9496 ['xs','sm','md','lg'].map(function(size){
9498 if(typeof(config[size]) == 'undefined'){
9502 if (!config[size]) { // 0 = hidden
9503 // BS 4 '0' is treated as hide that column and below.
9504 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9508 c.cls += ' col-' + size + '-' + config[size] + (
9509 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9517 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9528 renderBody : function()
9538 colspan : this.cm.getColumnCount()
9548 renderFooter : function()
9558 colspan : this.cm.getColumnCount()
9572 // Roo.log('ds onload');
9577 var ds = this.store;
9579 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9580 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9581 if (_this.store.sortInfo) {
9583 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9584 e.select('i', true).addClass(['fa-arrow-up']);
9587 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9588 e.select('i', true).addClass(['fa-arrow-down']);
9593 var tbody = this.bodyEl;
9595 if(ds.getCount() > 0){
9596 ds.data.each(function(d,rowIndex){
9597 var row = this.renderRow(cm, ds, rowIndex);
9599 tbody.createChild(row);
9603 if(row.cellObjects.length){
9604 Roo.each(row.cellObjects, function(r){
9605 _this.renderCellObject(r);
9612 var tfoot = this.el.select('tfoot', true).first();
9614 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9616 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9618 var total = this.ds.getTotalCount();
9620 if(this.footer.pageSize < total){
9621 this.mainFoot.show();
9625 Roo.each(this.el.select('tbody td', true).elements, function(e){
9626 e.on('mouseover', _this.onMouseover, _this);
9629 Roo.each(this.el.select('tbody td', true).elements, function(e){
9630 e.on('mouseout', _this.onMouseout, _this);
9632 this.fireEvent('rowsrendered', this);
9636 this.initCSS(); /// resize cols
9642 onUpdate : function(ds,record)
9644 this.refreshRow(record);
9648 onRemove : function(ds, record, index, isUpdate){
9649 if(isUpdate !== true){
9650 this.fireEvent("beforerowremoved", this, index, record);
9652 var bt = this.bodyEl.dom;
9654 var rows = this.el.select('tbody > tr', true).elements;
9656 if(typeof(rows[index]) != 'undefined'){
9657 bt.removeChild(rows[index].dom);
9660 // if(bt.rows[index]){
9661 // bt.removeChild(bt.rows[index]);
9664 if(isUpdate !== true){
9665 //this.stripeRows(index);
9666 //this.syncRowHeights(index, index);
9668 this.fireEvent("rowremoved", this, index, record);
9672 onAdd : function(ds, records, rowIndex)
9674 //Roo.log('on Add called');
9675 // - note this does not handle multiple adding very well..
9676 var bt = this.bodyEl.dom;
9677 for (var i =0 ; i < records.length;i++) {
9678 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9679 //Roo.log(records[i]);
9680 //Roo.log(this.store.getAt(rowIndex+i));
9681 this.insertRow(this.store, rowIndex + i, false);
9688 refreshRow : function(record){
9689 var ds = this.store, index;
9690 if(typeof record == 'number'){
9692 record = ds.getAt(index);
9694 index = ds.indexOf(record);
9696 return; // should not happen - but seems to
9699 this.insertRow(ds, index, true);
9701 this.onRemove(ds, record, index+1, true);
9703 //this.syncRowHeights(index, index);
9705 this.fireEvent("rowupdated", this, index, record);
9707 // private - called by RowSelection
9708 onRowSelect : function(rowIndex){
9709 var row = this.getRowDom(rowIndex);
9710 row.addClass(['bg-info','info']);
9712 // private - called by RowSelection
9713 onRowDeselect : function(rowIndex)
9718 var row = this.getRowDom(rowIndex);
9719 row.removeClass(['bg-info','info']);
9722 * Focuses the specified row.
9723 * @param {Number} row The row index
9725 focusRow : function(row)
9727 //Roo.log('GridView.focusRow');
9728 var x = this.bodyEl.dom.scrollLeft;
9729 this.focusCell(row, 0, false);
9730 this.bodyEl.dom.scrollLeft = x;
9734 * Focuses the specified cell.
9735 * @param {Number} row The row index
9736 * @param {Number} col The column index
9737 * @param {Boolean} hscroll false to disable horizontal scrolling
9739 focusCell : function(row, col, hscroll)
9741 //Roo.log('GridView.focusCell');
9742 var el = this.ensureVisible(row, col, hscroll);
9743 // not sure what focusEL achives = it's a <a> pos relative
9744 //this.focusEl.alignTo(el, "tl-tl");
9746 // this.focusEl.focus();
9748 // this.focusEl.focus.defer(1, this.focusEl);
9753 * Scrolls the specified cell into view
9754 * @param {Number} row The row index
9755 * @param {Number} col The column index
9756 * @param {Boolean} hscroll false to disable horizontal scrolling
9758 ensureVisible : function(row, col, hscroll)
9760 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9761 //return null; //disable for testing.
9762 if(typeof row != "number"){
9765 if(row < 0 && row >= this.ds.getCount()){
9768 col = (col !== undefined ? col : 0);
9770 while(cm.isHidden(col)){
9774 var el = this.getCellDom(row, col);
9778 var c = this.bodyEl.dom;
9780 var ctop = parseInt(el.offsetTop, 10);
9781 var cleft = parseInt(el.offsetLeft, 10);
9782 var cbot = ctop + el.offsetHeight;
9783 var cright = cleft + el.offsetWidth;
9785 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9786 var ch = 0; //?? header is not withing the area?
9787 var stop = parseInt(c.scrollTop, 10);
9788 var sleft = parseInt(c.scrollLeft, 10);
9789 var sbot = stop + ch;
9790 var sright = sleft + c.clientWidth;
9792 Roo.log('GridView.ensureVisible:' +
9794 ' c.clientHeight:' + c.clientHeight +
9795 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9804 //Roo.log("set scrolltop to ctop DISABLE?");
9805 }else if(cbot > sbot){
9806 //Roo.log("set scrolltop to cbot-ch");
9807 c.scrollTop = cbot-ch;
9810 if(hscroll !== false){
9812 c.scrollLeft = cleft;
9813 }else if(cright > sright){
9814 c.scrollLeft = cright-c.clientWidth;
9822 insertRow : function(dm, rowIndex, isUpdate){
9825 this.fireEvent("beforerowsinserted", this, rowIndex);
9827 //var s = this.getScrollState();
9828 var row = this.renderRow(this.cm, this.store, rowIndex);
9829 // insert before rowIndex..
9830 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9834 if(row.cellObjects.length){
9835 Roo.each(row.cellObjects, function(r){
9836 _this.renderCellObject(r);
9841 this.fireEvent("rowsinserted", this, rowIndex);
9842 //this.syncRowHeights(firstRow, lastRow);
9843 //this.stripeRows(firstRow);
9850 getRowDom : function(rowIndex)
9852 var rows = this.el.select('tbody > tr', true).elements;
9854 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9857 getCellDom : function(rowIndex, colIndex)
9859 var row = this.getRowDom(rowIndex);
9860 if (row === false) {
9863 var cols = row.select('td', true).elements;
9864 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9868 // returns the object tree for a tr..
9871 renderRow : function(cm, ds, rowIndex)
9873 var d = ds.getAt(rowIndex);
9877 cls : 'x-row-' + rowIndex,
9881 var cellObjects = [];
9883 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9884 var config = cm.config[i];
9886 var renderer = cm.getRenderer(i);
9890 if(typeof(renderer) !== 'undefined'){
9891 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9893 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9894 // and are rendered into the cells after the row is rendered - using the id for the element.
9896 if(typeof(value) === 'object'){
9906 rowIndex : rowIndex,
9911 this.fireEvent('rowclass', this, rowcfg);
9915 // this might end up displaying HTML?
9916 // this is too messy... - better to only do it on columsn you know are going to be too long
9917 //tooltip : (typeof(value) === 'object') ? '' : value,
9918 cls : rowcfg.rowClass + ' x-col-' + i,
9920 html: (typeof(value) === 'object') ? '' : value
9927 if(typeof(config.colspan) != 'undefined'){
9928 td.colspan = config.colspan;
9933 if(typeof(config.align) != 'undefined' && config.align.length){
9934 td.style += ' text-align:' + config.align + ';';
9936 if(typeof(config.valign) != 'undefined' && config.valign.length){
9937 td.style += ' vertical-align:' + config.valign + ';';
9940 if(typeof(config.width) != 'undefined'){
9941 td.style += ' width:' + config.width + 'px;';
9945 if(typeof(config.cursor) != 'undefined'){
9946 td.style += ' cursor:' + config.cursor + ';';
9949 if(typeof(config.cls) != 'undefined'){
9950 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9952 if (this.responsive) {
9953 ['xs','sm','md','lg'].map(function(size){
9955 if(typeof(config[size]) == 'undefined'){
9961 if (!config[size]) { // 0 = hidden
9962 // BS 4 '0' is treated as hide that column and below.
9963 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9967 td.cls += ' col-' + size + '-' + config[size] + (
9968 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9978 row.cellObjects = cellObjects;
9986 onBeforeLoad : function()
9995 this.el.select('tbody', true).first().dom.innerHTML = '';
9998 * Show or hide a row.
9999 * @param {Number} rowIndex to show or hide
10000 * @param {Boolean} state hide
10002 setRowVisibility : function(rowIndex, state)
10004 var bt = this.bodyEl.dom;
10006 var rows = this.el.select('tbody > tr', true).elements;
10008 if(typeof(rows[rowIndex]) == 'undefined'){
10011 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10016 getSelectionModel : function(){
10017 if(!this.selModel){
10018 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10020 return this.selModel;
10023 * Render the Roo.bootstrap object from renderder
10025 renderCellObject : function(r)
10029 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10031 var t = r.cfg.render(r.container);
10034 Roo.each(r.cfg.cn, function(c){
10036 container: t.getChildContainer(),
10039 _this.renderCellObject(child);
10044 * get the Row Index from a dom element.
10045 * @param {Roo.Element} row The row to look for
10046 * @returns {Number} the row
10048 getRowIndex : function(row)
10052 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10063 * get the header TH element for columnIndex
10064 * @param {Number} columnIndex
10065 * @returns {Roo.Element}
10067 getHeaderIndex: function(colIndex)
10069 var cols = this.headEl.select('th', true).elements;
10070 return cols[colIndex];
10073 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10074 * @param {domElement} cell to look for
10075 * @returns {Number} the column
10077 getCellIndex : function(cell)
10079 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10081 return parseInt(id[1], 10);
10086 * Returns the grid's underlying element = used by panel.Grid
10087 * @return {Element} The element
10089 getGridEl : function(){
10093 * Forces a resize - used by panel.Grid
10094 * @return {Element} The element
10096 autoSize : function()
10098 //var ctr = Roo.get(this.container.dom.parentElement);
10099 var ctr = Roo.get(this.el.dom);
10101 var thd = this.getGridEl().select('thead',true).first();
10102 var tbd = this.getGridEl().select('tbody', true).first();
10103 var tfd = this.getGridEl().select('tfoot', true).first();
10105 var cw = ctr.getWidth();
10106 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10110 tbd.setWidth(ctr.getWidth());
10111 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10112 // this needs fixing for various usage - currently only hydra job advers I think..
10114 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10116 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10119 cw = Math.max(cw, this.totalWidth);
10120 this.getGridEl().select('tbody tr',true).setWidth(cw);
10123 // resize 'expandable coloumn?
10125 return; // we doe not have a view in this design..
10128 onBodyScroll: function()
10130 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10132 this.headEl.setStyle({
10133 'position' : 'relative',
10134 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10140 var scrollHeight = this.bodyEl.dom.scrollHeight;
10142 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10144 var height = this.bodyEl.getHeight();
10146 if(scrollHeight - height == scrollTop) {
10148 var total = this.ds.getTotalCount();
10150 if(this.footer.cursor + this.footer.pageSize < total){
10152 this.footer.ds.load({
10154 start : this.footer.cursor + this.footer.pageSize,
10155 limit : this.footer.pageSize
10164 onColumnSplitterMoved : function(i, diff)
10166 this.userResized = true;
10168 var cm = this.colModel;
10170 var w = this.getHeaderIndex(i).getWidth() + diff;
10173 cm.setColumnWidth(i, w, true);
10175 //var cid = cm.getColumnId(i); << not used in this version?
10176 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10178 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10179 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10180 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10182 //this.updateSplitters();
10183 //this.layout(); << ??
10184 this.fireEvent("columnresize", i, w);
10186 onHeaderChange : function()
10188 var header = this.renderHeader();
10189 var table = this.el.select('table', true).first();
10191 this.headEl.remove();
10192 this.headEl = table.createChild(header, this.bodyEl, false);
10194 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10195 e.on('click', this.sort, this);
10198 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10199 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10204 onHiddenChange : function(colModel, colIndex, hidden)
10207 this.cm.setHidden()
10208 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10209 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10211 this.CSS.updateRule(thSelector, "display", "");
10212 this.CSS.updateRule(tdSelector, "display", "");
10215 this.CSS.updateRule(thSelector, "display", "none");
10216 this.CSS.updateRule(tdSelector, "display", "none");
10219 // onload calls initCSS()
10220 this.onHeaderChange();
10224 setColumnWidth: function(col_index, width)
10226 // width = "md-2 xs-2..."
10227 if(!this.colModel.config[col_index]) {
10231 var w = width.split(" ");
10233 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10235 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10238 for(var j = 0; j < w.length; j++) {
10244 var size_cls = w[j].split("-");
10246 if(!Number.isInteger(size_cls[1] * 1)) {
10250 if(!this.colModel.config[col_index][size_cls[0]]) {
10254 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10258 h_row[0].classList.replace(
10259 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10260 "col-"+size_cls[0]+"-"+size_cls[1]
10263 for(var i = 0; i < rows.length; i++) {
10265 var size_cls = w[j].split("-");
10267 if(!Number.isInteger(size_cls[1] * 1)) {
10271 if(!this.colModel.config[col_index][size_cls[0]]) {
10275 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10279 rows[i].classList.replace(
10280 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10281 "col-"+size_cls[0]+"-"+size_cls[1]
10285 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10290 // currently only used to find the split on drag..
10291 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10296 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10297 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10306 * @class Roo.bootstrap.TableCell
10307 * @extends Roo.bootstrap.Component
10308 * @children Roo.bootstrap.Component
10309 * @parent Roo.bootstrap.TableRow
10310 * Bootstrap TableCell class
10312 * @cfg {String} html cell contain text
10313 * @cfg {String} cls cell class
10314 * @cfg {String} tag cell tag (td|th) default td
10315 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10316 * @cfg {String} align Aligns the content in a cell
10317 * @cfg {String} axis Categorizes cells
10318 * @cfg {String} bgcolor Specifies the background color of a cell
10319 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10320 * @cfg {Number} colspan Specifies the number of columns a cell should span
10321 * @cfg {String} headers Specifies one or more header cells a cell is related to
10322 * @cfg {Number} height Sets the height of a cell
10323 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10324 * @cfg {Number} rowspan Sets the number of rows a cell should span
10325 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10326 * @cfg {String} valign Vertical aligns the content in a cell
10327 * @cfg {Number} width Specifies the width of a cell
10330 * Create a new TableCell
10331 * @param {Object} config The config object
10334 Roo.bootstrap.TableCell = function(config){
10335 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10338 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10358 getAutoCreate : function(){
10359 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10366 cfg.tag = this.tag;
10379 cfg.align=this.align
10384 if (this.bgcolor) {
10385 cfg.bgcolor=this.bgcolor
10387 if (this.charoff) {
10388 cfg.charoff=this.charoff
10390 if (this.colspan) {
10391 cfg.colspan=this.colspan
10393 if (this.headers) {
10394 cfg.headers=this.headers
10397 cfg.height=this.height
10400 cfg.nowrap=this.nowrap
10402 if (this.rowspan) {
10403 cfg.rowspan=this.rowspan
10406 cfg.scope=this.scope
10409 cfg.valign=this.valign
10412 cfg.width=this.width
10431 * @class Roo.bootstrap.TableRow
10432 * @extends Roo.bootstrap.Component
10433 * @children Roo.bootstrap.TableCell
10434 * @parent Roo.bootstrap.TableBody
10435 * Bootstrap TableRow class
10436 * @cfg {String} cls row class
10437 * @cfg {String} align Aligns the content in a table row
10438 * @cfg {String} bgcolor Specifies a background color for a table row
10439 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10440 * @cfg {String} valign Vertical aligns the content in a table row
10443 * Create a new TableRow
10444 * @param {Object} config The config object
10447 Roo.bootstrap.TableRow = function(config){
10448 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10451 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10459 getAutoCreate : function(){
10460 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10467 cfg.cls = this.cls;
10470 cfg.align = this.align;
10473 cfg.bgcolor = this.bgcolor;
10476 cfg.charoff = this.charoff;
10479 cfg.valign = this.valign;
10497 * @class Roo.bootstrap.TableBody
10498 * @extends Roo.bootstrap.Component
10499 * @children Roo.bootstrap.TableRow
10500 * @parent Roo.bootstrap.Table
10501 * Bootstrap TableBody class
10502 * @cfg {String} cls element class
10503 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10504 * @cfg {String} align Aligns the content inside the element
10505 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10506 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10509 * Create a new TableBody
10510 * @param {Object} config The config object
10513 Roo.bootstrap.TableBody = function(config){
10514 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10517 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10525 getAutoCreate : function(){
10526 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10536 cfg.tag = this.tag;
10540 cfg.align = this.align;
10543 cfg.charoff = this.charoff;
10546 cfg.valign = this.valign;
10553 // initEvents : function()
10556 // if(!this.store){
10560 // this.store = Roo.factory(this.store, Roo.data);
10561 // this.store.on('load', this.onLoad, this);
10563 // this.store.load();
10567 // onLoad: function ()
10569 // this.fireEvent('load', this);
10579 * Ext JS Library 1.1.1
10580 * Copyright(c) 2006-2007, Ext JS, LLC.
10582 * Originally Released Under LGPL - original licence link has changed is not relivant.
10585 * <script type="text/javascript">
10588 // as we use this in bootstrap.
10589 Roo.namespace('Roo.form');
10591 * @class Roo.form.Action
10592 * Internal Class used to handle form actions
10594 * @param {Roo.form.BasicForm} el The form element or its id
10595 * @param {Object} config Configuration options
10600 // define the action interface
10601 Roo.form.Action = function(form, options){
10603 this.options = options || {};
10606 * Client Validation Failed
10609 Roo.form.Action.CLIENT_INVALID = 'client';
10611 * Server Validation Failed
10614 Roo.form.Action.SERVER_INVALID = 'server';
10616 * Connect to Server Failed
10619 Roo.form.Action.CONNECT_FAILURE = 'connect';
10621 * Reading Data from Server Failed
10624 Roo.form.Action.LOAD_FAILURE = 'load';
10626 Roo.form.Action.prototype = {
10628 failureType : undefined,
10629 response : undefined,
10630 result : undefined,
10632 // interface method
10633 run : function(options){
10637 // interface method
10638 success : function(response){
10642 // interface method
10643 handleResponse : function(response){
10647 // default connection failure
10648 failure : function(response){
10650 this.response = response;
10651 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10652 this.form.afterAction(this, false);
10655 processResponse : function(response){
10656 this.response = response;
10657 if(!response.responseText){
10660 this.result = this.handleResponse(response);
10661 return this.result;
10664 // utility functions used internally
10665 getUrl : function(appendParams){
10666 var url = this.options.url || this.form.url || this.form.el.dom.action;
10668 var p = this.getParams();
10670 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10676 getMethod : function(){
10677 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10680 getParams : function(){
10681 var bp = this.form.baseParams;
10682 var p = this.options.params;
10684 if(typeof p == "object"){
10685 p = Roo.urlEncode(Roo.applyIf(p, bp));
10686 }else if(typeof p == 'string' && bp){
10687 p += '&' + Roo.urlEncode(bp);
10690 p = Roo.urlEncode(bp);
10695 createCallback : function(){
10697 success: this.success,
10698 failure: this.failure,
10700 timeout: (this.form.timeout*1000),
10701 upload: this.form.fileUpload ? this.success : undefined
10706 Roo.form.Action.Submit = function(form, options){
10707 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10710 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10713 haveProgress : false,
10714 uploadComplete : false,
10716 // uploadProgress indicator.
10717 uploadProgress : function()
10719 if (!this.form.progressUrl) {
10723 if (!this.haveProgress) {
10724 Roo.MessageBox.progress("Uploading", "Uploading");
10726 if (this.uploadComplete) {
10727 Roo.MessageBox.hide();
10731 this.haveProgress = true;
10733 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10735 var c = new Roo.data.Connection();
10737 url : this.form.progressUrl,
10742 success : function(req){
10743 //console.log(data);
10747 rdata = Roo.decode(req.responseText)
10749 Roo.log("Invalid data from server..");
10753 if (!rdata || !rdata.success) {
10755 Roo.MessageBox.alert(Roo.encode(rdata));
10758 var data = rdata.data;
10760 if (this.uploadComplete) {
10761 Roo.MessageBox.hide();
10766 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10767 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10770 this.uploadProgress.defer(2000,this);
10773 failure: function(data) {
10774 Roo.log('progress url failed ');
10785 // run get Values on the form, so it syncs any secondary forms.
10786 this.form.getValues();
10788 var o = this.options;
10789 var method = this.getMethod();
10790 var isPost = method == 'POST';
10791 if(o.clientValidation === false || this.form.isValid()){
10793 if (this.form.progressUrl) {
10794 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10795 (new Date() * 1) + '' + Math.random());
10800 Roo.Ajax.request(Roo.apply(this.createCallback(), {
10801 form:this.form.el.dom,
10802 url:this.getUrl(!isPost),
10804 params:isPost ? this.getParams() : null,
10805 isUpload: this.form.fileUpload,
10806 formData : this.form.formData
10809 this.uploadProgress();
10811 }else if (o.clientValidation !== false){ // client validation failed
10812 this.failureType = Roo.form.Action.CLIENT_INVALID;
10813 this.form.afterAction(this, false);
10817 success : function(response)
10819 this.uploadComplete= true;
10820 if (this.haveProgress) {
10821 Roo.MessageBox.hide();
10825 var result = this.processResponse(response);
10826 if(result === true || result.success){
10827 this.form.afterAction(this, true);
10831 this.form.markInvalid(result.errors);
10832 this.failureType = Roo.form.Action.SERVER_INVALID;
10834 this.form.afterAction(this, false);
10836 failure : function(response)
10838 this.uploadComplete= true;
10839 if (this.haveProgress) {
10840 Roo.MessageBox.hide();
10843 this.response = response;
10844 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10845 this.form.afterAction(this, false);
10848 handleResponse : function(response){
10849 if(this.form.errorReader){
10850 var rs = this.form.errorReader.read(response);
10853 for(var i = 0, len = rs.records.length; i < len; i++) {
10854 var r = rs.records[i];
10855 errors[i] = r.data;
10858 if(errors.length < 1){
10862 success : rs.success,
10868 ret = Roo.decode(response.responseText);
10872 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10882 Roo.form.Action.Load = function(form, options){
10883 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10884 this.reader = this.form.reader;
10887 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10892 Roo.Ajax.request(Roo.apply(
10893 this.createCallback(), {
10894 method:this.getMethod(),
10895 url:this.getUrl(false),
10896 params:this.getParams()
10900 success : function(response){
10902 var result = this.processResponse(response);
10903 if(result === true || !result.success || !result.data){
10904 this.failureType = Roo.form.Action.LOAD_FAILURE;
10905 this.form.afterAction(this, false);
10908 this.form.clearInvalid();
10909 this.form.setValues(result.data);
10910 this.form.afterAction(this, true);
10913 handleResponse : function(response){
10914 if(this.form.reader){
10915 var rs = this.form.reader.read(response);
10916 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10918 success : rs.success,
10922 return Roo.decode(response.responseText);
10926 Roo.form.Action.ACTION_TYPES = {
10927 'load' : Roo.form.Action.Load,
10928 'submit' : Roo.form.Action.Submit
10937 * @class Roo.bootstrap.Form
10938 * @extends Roo.bootstrap.Component
10939 * @children Roo.bootstrap.Component
10940 * Bootstrap Form class
10941 * @cfg {String} method GET | POST (default POST)
10942 * @cfg {String} labelAlign top | left (default top)
10943 * @cfg {String} align left | right - for navbars
10944 * @cfg {Boolean} loadMask load mask when submit (default true)
10948 * Create a new Form
10949 * @param {Object} config The config object
10953 Roo.bootstrap.Form = function(config){
10955 Roo.bootstrap.Form.superclass.constructor.call(this, config);
10957 Roo.bootstrap.Form.popover.apply();
10961 * @event clientvalidation
10962 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10963 * @param {Form} this
10964 * @param {Boolean} valid true if the form has passed client-side validation
10966 clientvalidation: true,
10968 * @event beforeaction
10969 * Fires before any action is performed. Return false to cancel the action.
10970 * @param {Form} this
10971 * @param {Action} action The action to be performed
10973 beforeaction: true,
10975 * @event actionfailed
10976 * Fires when an action fails.
10977 * @param {Form} this
10978 * @param {Action} action The action that failed
10980 actionfailed : true,
10982 * @event actioncomplete
10983 * Fires when an action is completed.
10984 * @param {Form} this
10985 * @param {Action} action The action that completed
10987 actioncomplete : true
10991 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
10994 * @cfg {String} method
10995 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10999 * @cfg {String} url
11000 * The URL to use for form actions if one isn't supplied in the action options.
11003 * @cfg {Boolean} fileUpload
11004 * Set to true if this form is a file upload.
11008 * @cfg {Object} baseParams
11009 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11013 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11017 * @cfg {Sting} align (left|right) for navbar forms
11022 activeAction : null,
11025 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11026 * element by passing it or its id or mask the form itself by passing in true.
11029 waitMsgTarget : false,
11034 * @cfg {Boolean} errorMask (true|false) default false
11039 * @cfg {Number} maskOffset Default 100
11044 * @cfg {Boolean} maskBody
11048 getAutoCreate : function(){
11052 method : this.method || 'POST',
11053 id : this.id || Roo.id(),
11056 if (this.parent().xtype.match(/^Nav/)) {
11057 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11061 if (this.labelAlign == 'left' ) {
11062 cfg.cls += ' form-horizontal';
11068 initEvents : function()
11070 this.el.on('submit', this.onSubmit, this);
11071 // this was added as random key presses on the form where triggering form submit.
11072 this.el.on('keypress', function(e) {
11073 if (e.getCharCode() != 13) {
11076 // we might need to allow it for textareas.. and some other items.
11077 // check e.getTarget().
11079 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11083 Roo.log("keypress blocked");
11085 e.preventDefault();
11091 onSubmit : function(e){
11096 * Returns true if client-side validation on the form is successful.
11099 isValid : function(){
11100 var items = this.getItems();
11102 var target = false;
11104 items.each(function(f){
11110 Roo.log('invalid field: ' + f.name);
11114 if(!target && f.el.isVisible(true)){
11120 if(this.errorMask && !valid){
11121 Roo.bootstrap.Form.popover.mask(this, target);
11128 * Returns true if any fields in this form have changed since their original load.
11131 isDirty : function(){
11133 var items = this.getItems();
11134 items.each(function(f){
11144 * Performs a predefined action (submit or load) or custom actions you define on this form.
11145 * @param {String} actionName The name of the action type
11146 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11147 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11148 * accept other config options):
11150 Property Type Description
11151 ---------------- --------------- ----------------------------------------------------------------------------------
11152 url String The url for the action (defaults to the form's url)
11153 method String The form method to use (defaults to the form's method, or POST if not defined)
11154 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11155 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11156 validate the form on the client (defaults to false)
11158 * @return {BasicForm} this
11160 doAction : function(action, options){
11161 if(typeof action == 'string'){
11162 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11164 if(this.fireEvent('beforeaction', this, action) !== false){
11165 this.beforeAction(action);
11166 action.run.defer(100, action);
11172 beforeAction : function(action){
11173 var o = action.options;
11178 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11180 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11183 // not really supported yet.. ??
11185 //if(this.waitMsgTarget === true){
11186 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11187 //}else if(this.waitMsgTarget){
11188 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11189 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11191 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11197 afterAction : function(action, success){
11198 this.activeAction = null;
11199 var o = action.options;
11204 Roo.get(document.body).unmask();
11210 //if(this.waitMsgTarget === true){
11211 // this.el.unmask();
11212 //}else if(this.waitMsgTarget){
11213 // this.waitMsgTarget.unmask();
11215 // Roo.MessageBox.updateProgress(1);
11216 // Roo.MessageBox.hide();
11223 Roo.callback(o.success, o.scope, [this, action]);
11224 this.fireEvent('actioncomplete', this, action);
11228 // failure condition..
11229 // we have a scenario where updates need confirming.
11230 // eg. if a locking scenario exists..
11231 // we look for { errors : { needs_confirm : true }} in the response.
11233 (typeof(action.result) != 'undefined') &&
11234 (typeof(action.result.errors) != 'undefined') &&
11235 (typeof(action.result.errors.needs_confirm) != 'undefined')
11238 Roo.log("not supported yet");
11241 Roo.MessageBox.confirm(
11242 "Change requires confirmation",
11243 action.result.errorMsg,
11248 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11258 Roo.callback(o.failure, o.scope, [this, action]);
11259 // show an error message if no failed handler is set..
11260 if (!this.hasListener('actionfailed')) {
11261 Roo.log("need to add dialog support");
11263 Roo.MessageBox.alert("Error",
11264 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11265 action.result.errorMsg :
11266 "Saving Failed, please check your entries or try again"
11271 this.fireEvent('actionfailed', this, action);
11276 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11277 * @param {String} id The value to search for
11280 findField : function(id){
11281 var items = this.getItems();
11282 var field = items.get(id);
11284 items.each(function(f){
11285 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11292 return field || null;
11295 * Mark fields in this form invalid in bulk.
11296 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11297 * @return {BasicForm} this
11299 markInvalid : function(errors){
11300 if(errors instanceof Array){
11301 for(var i = 0, len = errors.length; i < len; i++){
11302 var fieldError = errors[i];
11303 var f = this.findField(fieldError.id);
11305 f.markInvalid(fieldError.msg);
11311 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11312 field.markInvalid(errors[id]);
11316 //Roo.each(this.childForms || [], function (f) {
11317 // f.markInvalid(errors);
11324 * Set values for fields in this form in bulk.
11325 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11326 * @return {BasicForm} this
11328 setValues : function(values){
11329 if(values instanceof Array){ // array of objects
11330 for(var i = 0, len = values.length; i < len; i++){
11332 var f = this.findField(v.id);
11334 f.setValue(v.value);
11335 if(this.trackResetOnLoad){
11336 f.originalValue = f.getValue();
11340 }else{ // object hash
11343 if(typeof values[id] != 'function' && (field = this.findField(id))){
11345 if (field.setFromData &&
11346 field.valueField &&
11347 field.displayField &&
11348 // combos' with local stores can
11349 // be queried via setValue()
11350 // to set their value..
11351 (field.store && !field.store.isLocal)
11355 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11356 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11357 field.setFromData(sd);
11359 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11361 field.setFromData(values);
11364 field.setValue(values[id]);
11368 if(this.trackResetOnLoad){
11369 field.originalValue = field.getValue();
11375 //Roo.each(this.childForms || [], function (f) {
11376 // f.setValues(values);
11383 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11384 * they are returned as an array.
11385 * @param {Boolean} asString
11388 getValues : function(asString){
11389 //if (this.childForms) {
11390 // copy values from the child forms
11391 // Roo.each(this.childForms, function (f) {
11392 // this.setValues(f.getValues());
11398 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11399 if(asString === true){
11402 return Roo.urlDecode(fs);
11406 * Returns the fields in this form as an object with key/value pairs.
11407 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11410 getFieldValues : function(with_hidden)
11412 var items = this.getItems();
11414 items.each(function(f){
11416 if (!f.getName()) {
11420 var v = f.getValue();
11422 if (f.inputType =='radio') {
11423 if (typeof(ret[f.getName()]) == 'undefined') {
11424 ret[f.getName()] = ''; // empty..
11427 if (!f.el.dom.checked) {
11431 v = f.el.dom.value;
11435 if(f.xtype == 'MoneyField'){
11436 ret[f.currencyName] = f.getCurrency();
11439 // not sure if this supported any more..
11440 if ((typeof(v) == 'object') && f.getRawValue) {
11441 v = f.getRawValue() ; // dates..
11443 // combo boxes where name != hiddenName...
11444 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11445 ret[f.name] = f.getRawValue();
11447 ret[f.getName()] = v;
11454 * Clears all invalid messages in this form.
11455 * @return {BasicForm} this
11457 clearInvalid : function(){
11458 var items = this.getItems();
11460 items.each(function(f){
11468 * Resets this form.
11469 * @return {BasicForm} this
11471 reset : function(){
11472 var items = this.getItems();
11473 items.each(function(f){
11477 Roo.each(this.childForms || [], function (f) {
11485 getItems : function()
11487 var r=new Roo.util.MixedCollection(false, function(o){
11488 return o.id || (o.id = Roo.id());
11490 var iter = function(el) {
11497 Roo.each(el.items,function(e) {
11506 hideFields : function(items)
11508 Roo.each(items, function(i){
11510 var f = this.findField(i);
11521 showFields : function(items)
11523 Roo.each(items, function(i){
11525 var f = this.findField(i);
11538 Roo.apply(Roo.bootstrap.Form, {
11554 intervalID : false,
11560 if(this.isApplied){
11565 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11566 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11567 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11568 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11571 this.maskEl.top.enableDisplayMode("block");
11572 this.maskEl.left.enableDisplayMode("block");
11573 this.maskEl.bottom.enableDisplayMode("block");
11574 this.maskEl.right.enableDisplayMode("block");
11576 this.toolTip = new Roo.bootstrap.Tooltip({
11577 cls : 'roo-form-error-popover',
11579 'left' : ['r-l', [-2,0], 'right'],
11580 'right' : ['l-r', [2,0], 'left'],
11581 'bottom' : ['tl-bl', [0,2], 'top'],
11582 'top' : [ 'bl-tl', [0,-2], 'bottom']
11586 this.toolTip.render(Roo.get(document.body));
11588 this.toolTip.el.enableDisplayMode("block");
11590 Roo.get(document.body).on('click', function(){
11594 Roo.get(document.body).on('touchstart', function(){
11598 this.isApplied = true
11601 mask : function(form, target)
11605 this.target = target;
11607 if(!this.form.errorMask || !target.el){
11611 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11613 Roo.log(scrollable);
11615 var ot = this.target.el.calcOffsetsTo(scrollable);
11617 var scrollTo = ot[1] - this.form.maskOffset;
11619 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11621 scrollable.scrollTo('top', scrollTo);
11623 var box = this.target.el.getBox();
11625 var zIndex = Roo.bootstrap.Modal.zIndex++;
11628 this.maskEl.top.setStyle('position', 'absolute');
11629 this.maskEl.top.setStyle('z-index', zIndex);
11630 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11631 this.maskEl.top.setLeft(0);
11632 this.maskEl.top.setTop(0);
11633 this.maskEl.top.show();
11635 this.maskEl.left.setStyle('position', 'absolute');
11636 this.maskEl.left.setStyle('z-index', zIndex);
11637 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11638 this.maskEl.left.setLeft(0);
11639 this.maskEl.left.setTop(box.y - this.padding);
11640 this.maskEl.left.show();
11642 this.maskEl.bottom.setStyle('position', 'absolute');
11643 this.maskEl.bottom.setStyle('z-index', zIndex);
11644 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11645 this.maskEl.bottom.setLeft(0);
11646 this.maskEl.bottom.setTop(box.bottom + this.padding);
11647 this.maskEl.bottom.show();
11649 this.maskEl.right.setStyle('position', 'absolute');
11650 this.maskEl.right.setStyle('z-index', zIndex);
11651 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11652 this.maskEl.right.setLeft(box.right + this.padding);
11653 this.maskEl.right.setTop(box.y - this.padding);
11654 this.maskEl.right.show();
11656 this.toolTip.bindEl = this.target.el;
11658 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11660 var tip = this.target.blankText;
11662 if(this.target.getValue() !== '' ) {
11664 if (this.target.invalidText.length) {
11665 tip = this.target.invalidText;
11666 } else if (this.target.regexText.length){
11667 tip = this.target.regexText;
11671 this.toolTip.show(tip);
11673 this.intervalID = window.setInterval(function() {
11674 Roo.bootstrap.Form.popover.unmask();
11677 window.onwheel = function(){ return false;};
11679 (function(){ this.isMasked = true; }).defer(500, this);
11683 unmask : function()
11685 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11689 this.maskEl.top.setStyle('position', 'absolute');
11690 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11691 this.maskEl.top.hide();
11693 this.maskEl.left.setStyle('position', 'absolute');
11694 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11695 this.maskEl.left.hide();
11697 this.maskEl.bottom.setStyle('position', 'absolute');
11698 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11699 this.maskEl.bottom.hide();
11701 this.maskEl.right.setStyle('position', 'absolute');
11702 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11703 this.maskEl.right.hide();
11705 this.toolTip.hide();
11707 this.toolTip.el.hide();
11709 window.onwheel = function(){ return true;};
11711 if(this.intervalID){
11712 window.clearInterval(this.intervalID);
11713 this.intervalID = false;
11716 this.isMasked = false;
11726 * Ext JS Library 1.1.1
11727 * Copyright(c) 2006-2007, Ext JS, LLC.
11729 * Originally Released Under LGPL - original licence link has changed is not relivant.
11732 * <script type="text/javascript">
11735 * @class Roo.form.VTypes
11736 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11739 Roo.form.VTypes = function(){
11740 // closure these in so they are only created once.
11741 var alpha = /^[a-zA-Z_]+$/;
11742 var alphanum = /^[a-zA-Z0-9_]+$/;
11743 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11744 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11746 // All these messages and functions are configurable
11749 * The function used to validate email addresses
11750 * @param {String} value The email address
11752 'email' : function(v){
11753 return email.test(v);
11756 * The error text to display when the email validation function returns false
11759 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11761 * The keystroke filter mask to be applied on email input
11764 'emailMask' : /[a-z0-9_\.\-@]/i,
11767 * The function used to validate URLs
11768 * @param {String} value The URL
11770 'url' : function(v){
11771 return url.test(v);
11774 * The error text to display when the url validation function returns false
11777 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11780 * The function used to validate alpha values
11781 * @param {String} value The value
11783 'alpha' : function(v){
11784 return alpha.test(v);
11787 * The error text to display when the alpha validation function returns false
11790 'alphaText' : 'This field should only contain letters and _',
11792 * The keystroke filter mask to be applied on alpha input
11795 'alphaMask' : /[a-z_]/i,
11798 * The function used to validate alphanumeric values
11799 * @param {String} value The value
11801 'alphanum' : function(v){
11802 return alphanum.test(v);
11805 * The error text to display when the alphanumeric validation function returns false
11808 'alphanumText' : 'This field should only contain letters, numbers and _',
11810 * The keystroke filter mask to be applied on alphanumeric input
11813 'alphanumMask' : /[a-z0-9_]/i
11823 * @class Roo.bootstrap.Input
11824 * @extends Roo.bootstrap.Component
11825 * Bootstrap Input class
11826 * @cfg {Boolean} disabled is it disabled
11827 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
11828 * @cfg {String} name name of the input
11829 * @cfg {string} fieldLabel - the label associated
11830 * @cfg {string} placeholder - placeholder to put in text.
11831 * @cfg {string} before - input group add on before
11832 * @cfg {string} after - input group add on after
11833 * @cfg {string} size - (lg|sm) or leave empty..
11834 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11835 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11836 * @cfg {Number} md colspan out of 12 for computer-sized screens
11837 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11838 * @cfg {string} value default value of the input
11839 * @cfg {Number} labelWidth set the width of label
11840 * @cfg {Number} labellg set the width of label (1-12)
11841 * @cfg {Number} labelmd set the width of label (1-12)
11842 * @cfg {Number} labelsm set the width of label (1-12)
11843 * @cfg {Number} labelxs set the width of label (1-12)
11844 * @cfg {String} labelAlign (top|left)
11845 * @cfg {Boolean} readOnly Specifies that the field should be read-only
11846 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11847 * @cfg {String} indicatorpos (left|right) default left
11848 * @cfg {String} capture (user|camera) use for file input only. (default empty)
11849 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11850 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11852 * @cfg {String} align (left|center|right) Default left
11853 * @cfg {Boolean} forceFeedback (true|false) Default false
11856 * Create a new Input
11857 * @param {Object} config The config object
11860 Roo.bootstrap.Input = function(config){
11862 Roo.bootstrap.Input.superclass.constructor.call(this, config);
11867 * Fires when this field receives input focus.
11868 * @param {Roo.form.Field} this
11873 * Fires when this field loses input focus.
11874 * @param {Roo.form.Field} this
11878 * @event specialkey
11879 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
11880 * {@link Roo.EventObject#getKey} to determine which key was pressed.
11881 * @param {Roo.form.Field} this
11882 * @param {Roo.EventObject} e The event object
11887 * Fires just before the field blurs if the field value has changed.
11888 * @param {Roo.form.Field} this
11889 * @param {Mixed} newValue The new value
11890 * @param {Mixed} oldValue The original value
11895 * Fires after the field has been marked as invalid.
11896 * @param {Roo.form.Field} this
11897 * @param {String} msg The validation message
11902 * Fires after the field has been validated with no errors.
11903 * @param {Roo.form.Field} this
11908 * Fires after the key up
11909 * @param {Roo.form.Field} this
11910 * @param {Roo.EventObject} e The event Object
11915 * Fires after the user pastes into input
11916 * @param {Roo.form.Field} this
11917 * @param {Roo.EventObject} e The event Object
11923 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
11925 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11926 automatic validation (defaults to "keyup").
11928 validationEvent : "keyup",
11930 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11932 validateOnBlur : true,
11934 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11936 validationDelay : 250,
11938 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11940 focusClass : "x-form-focus", // not needed???
11944 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11946 invalidClass : "has-warning",
11949 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11951 validClass : "has-success",
11954 * @cfg {Boolean} hasFeedback (true|false) default true
11956 hasFeedback : true,
11959 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11961 invalidFeedbackClass : "glyphicon-warning-sign",
11964 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11966 validFeedbackClass : "glyphicon-ok",
11969 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11971 selectOnFocus : false,
11974 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11978 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11983 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11985 disableKeyFilter : false,
11988 * @cfg {Boolean} disabled True to disable the field (defaults to false).
11992 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11996 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11998 blankText : "Please complete this mandatory field",
12001 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12005 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12007 maxLength : Number.MAX_VALUE,
12009 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12011 minLengthText : "The minimum length for this field is {0}",
12013 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12015 maxLengthText : "The maximum length for this field is {0}",
12019 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12020 * If available, this function will be called only after the basic validators all return true, and will be passed the
12021 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12025 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12026 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12027 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
12031 * @cfg {String} regexText -- Depricated - use Invalid Text
12036 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12042 autocomplete: false,
12046 inputType : 'text',
12049 placeholder: false,
12054 preventMark: false,
12055 isFormField : true,
12058 labelAlign : false,
12061 formatedValue : false,
12062 forceFeedback : false,
12064 indicatorpos : 'left',
12074 parentLabelAlign : function()
12077 while (parent.parent()) {
12078 parent = parent.parent();
12079 if (typeof(parent.labelAlign) !='undefined') {
12080 return parent.labelAlign;
12087 getAutoCreate : function()
12089 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12095 if(this.inputType != 'hidden'){
12096 cfg.cls = 'form-group' //input-group
12102 type : this.inputType,
12103 value : this.value,
12104 cls : 'form-control',
12105 placeholder : this.placeholder || '',
12106 autocomplete : this.autocomplete || 'new-password'
12108 if (this.inputType == 'file') {
12109 input.style = 'overflow:hidden'; // why not in CSS?
12112 if(this.capture.length){
12113 input.capture = this.capture;
12116 if(this.accept.length){
12117 input.accept = this.accept + "/*";
12121 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12124 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12125 input.maxLength = this.maxLength;
12128 if (this.disabled) {
12129 input.disabled=true;
12132 if (this.readOnly) {
12133 input.readonly=true;
12137 input.name = this.name;
12141 input.cls += ' input-' + this.size;
12145 ['xs','sm','md','lg'].map(function(size){
12146 if (settings[size]) {
12147 cfg.cls += ' col-' + size + '-' + settings[size];
12151 var inputblock = input;
12155 cls: 'glyphicon form-control-feedback'
12158 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12161 cls : 'has-feedback',
12169 if (this.before || this.after) {
12172 cls : 'input-group',
12176 if (this.before && typeof(this.before) == 'string') {
12178 inputblock.cn.push({
12180 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12184 if (this.before && typeof(this.before) == 'object') {
12185 this.before = Roo.factory(this.before);
12187 inputblock.cn.push({
12189 cls : 'roo-input-before input-group-prepend input-group-' +
12190 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12194 inputblock.cn.push(input);
12196 if (this.after && typeof(this.after) == 'string') {
12197 inputblock.cn.push({
12199 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12203 if (this.after && typeof(this.after) == 'object') {
12204 this.after = Roo.factory(this.after);
12206 inputblock.cn.push({
12208 cls : 'roo-input-after input-group-append input-group-' +
12209 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12213 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12214 inputblock.cls += ' has-feedback';
12215 inputblock.cn.push(feedback);
12220 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12221 tooltip : 'This field is required'
12223 if (this.allowBlank ) {
12224 indicator.style = this.allowBlank ? ' display:none' : '';
12226 if (align ==='left' && this.fieldLabel.length) {
12228 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12235 cls : 'control-label col-form-label',
12236 html : this.fieldLabel
12247 var labelCfg = cfg.cn[1];
12248 var contentCfg = cfg.cn[2];
12250 if(this.indicatorpos == 'right'){
12255 cls : 'control-label col-form-label',
12259 html : this.fieldLabel
12273 labelCfg = cfg.cn[0];
12274 contentCfg = cfg.cn[1];
12278 if(this.labelWidth > 12){
12279 labelCfg.style = "width: " + this.labelWidth + 'px';
12282 if(this.labelWidth < 13 && this.labelmd == 0){
12283 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12286 if(this.labellg > 0){
12287 labelCfg.cls += ' col-lg-' + this.labellg;
12288 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12291 if(this.labelmd > 0){
12292 labelCfg.cls += ' col-md-' + this.labelmd;
12293 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12296 if(this.labelsm > 0){
12297 labelCfg.cls += ' col-sm-' + this.labelsm;
12298 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12301 if(this.labelxs > 0){
12302 labelCfg.cls += ' col-xs-' + this.labelxs;
12303 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12307 } else if ( this.fieldLabel.length) {
12314 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12315 tooltip : 'This field is required',
12316 style : this.allowBlank ? ' display:none' : ''
12320 //cls : 'input-group-addon',
12321 html : this.fieldLabel
12329 if(this.indicatorpos == 'right'){
12334 //cls : 'input-group-addon',
12335 html : this.fieldLabel
12340 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12341 tooltip : 'This field is required',
12342 style : this.allowBlank ? ' display:none' : ''
12362 if (this.parentType === 'Navbar' && this.parent().bar) {
12363 cfg.cls += ' navbar-form';
12366 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12367 // on BS4 we do this only if not form
12368 cfg.cls += ' navbar-form';
12376 * return the real input element.
12378 inputEl: function ()
12380 return this.el.select('input.form-control',true).first();
12383 tooltipEl : function()
12385 return this.inputEl();
12388 indicatorEl : function()
12390 if (Roo.bootstrap.version == 4) {
12391 return false; // not enabled in v4 yet.
12394 var indicator = this.el.select('i.roo-required-indicator',true).first();
12404 setDisabled : function(v)
12406 var i = this.inputEl().dom;
12408 i.removeAttribute('disabled');
12412 i.setAttribute('disabled','true');
12414 initEvents : function()
12417 this.inputEl().on("keydown" , this.fireKey, this);
12418 this.inputEl().on("focus", this.onFocus, this);
12419 this.inputEl().on("blur", this.onBlur, this);
12421 this.inputEl().relayEvent('keyup', this);
12422 this.inputEl().relayEvent('paste', this);
12424 this.indicator = this.indicatorEl();
12426 if(this.indicator){
12427 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12430 // reference to original value for reset
12431 this.originalValue = this.getValue();
12432 //Roo.form.TextField.superclass.initEvents.call(this);
12433 if(this.validationEvent == 'keyup'){
12434 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12435 this.inputEl().on('keyup', this.filterValidation, this);
12437 else if(this.validationEvent !== false){
12438 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12441 if(this.selectOnFocus){
12442 this.on("focus", this.preFocus, this);
12445 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12446 this.inputEl().on("keypress", this.filterKeys, this);
12448 this.inputEl().relayEvent('keypress', this);
12451 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12452 this.el.on("click", this.autoSize, this);
12455 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12456 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12459 if (typeof(this.before) == 'object') {
12460 this.before.render(this.el.select('.roo-input-before',true).first());
12462 if (typeof(this.after) == 'object') {
12463 this.after.render(this.el.select('.roo-input-after',true).first());
12466 this.inputEl().on('change', this.onChange, this);
12469 filterValidation : function(e){
12470 if(!e.isNavKeyPress()){
12471 this.validationTask.delay(this.validationDelay);
12475 * Validates the field value
12476 * @return {Boolean} True if the value is valid, else false
12478 validate : function(){
12479 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12480 if(this.disabled || this.validateValue(this.getRawValue())){
12485 this.markInvalid();
12491 * Validates a value according to the field's validation rules and marks the field as invalid
12492 * if the validation fails
12493 * @param {Mixed} value The value to validate
12494 * @return {Boolean} True if the value is valid, else false
12496 validateValue : function(value)
12498 if(this.getVisibilityEl().hasClass('hidden')){
12502 if(value.length < 1) { // if it's blank
12503 if(this.allowBlank){
12509 if(value.length < this.minLength){
12512 if(value.length > this.maxLength){
12516 var vt = Roo.form.VTypes;
12517 if(!vt[this.vtype](value, this)){
12521 if(typeof this.validator == "function"){
12522 var msg = this.validator(value);
12526 if (typeof(msg) == 'string') {
12527 this.invalidText = msg;
12531 if(this.regex && !this.regex.test(value)){
12539 fireKey : function(e){
12540 //Roo.log('field ' + e.getKey());
12541 if(e.isNavKeyPress()){
12542 this.fireEvent("specialkey", this, e);
12545 focus : function (selectText){
12547 this.inputEl().focus();
12548 if(selectText === true){
12549 this.inputEl().dom.select();
12555 onFocus : function(){
12556 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12557 // this.el.addClass(this.focusClass);
12559 if(!this.hasFocus){
12560 this.hasFocus = true;
12561 this.startValue = this.getValue();
12562 this.fireEvent("focus", this);
12566 beforeBlur : Roo.emptyFn,
12570 onBlur : function(){
12572 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12573 //this.el.removeClass(this.focusClass);
12575 this.hasFocus = false;
12576 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12579 var v = this.getValue();
12580 if(String(v) !== String(this.startValue)){
12581 this.fireEvent('change', this, v, this.startValue);
12583 this.fireEvent("blur", this);
12586 onChange : function(e)
12588 var v = this.getValue();
12589 if(String(v) !== String(this.startValue)){
12590 this.fireEvent('change', this, v, this.startValue);
12596 * Resets the current field value to the originally loaded value and clears any validation messages
12598 reset : function(){
12599 this.setValue(this.originalValue);
12603 * Returns the name of the field
12604 * @return {Mixed} name The name field
12606 getName: function(){
12610 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12611 * @return {Mixed} value The field value
12613 getValue : function(){
12615 var v = this.inputEl().getValue();
12620 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
12621 * @return {Mixed} value The field value
12623 getRawValue : function(){
12624 var v = this.inputEl().getValue();
12630 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
12631 * @param {Mixed} value The value to set
12633 setRawValue : function(v){
12634 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12637 selectText : function(start, end){
12638 var v = this.getRawValue();
12640 start = start === undefined ? 0 : start;
12641 end = end === undefined ? v.length : end;
12642 var d = this.inputEl().dom;
12643 if(d.setSelectionRange){
12644 d.setSelectionRange(start, end);
12645 }else if(d.createTextRange){
12646 var range = d.createTextRange();
12647 range.moveStart("character", start);
12648 range.moveEnd("character", v.length-end);
12655 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
12656 * @param {Mixed} value The value to set
12658 setValue : function(v){
12661 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12667 processValue : function(value){
12668 if(this.stripCharsRe){
12669 var newValue = value.replace(this.stripCharsRe, '');
12670 if(newValue !== value){
12671 this.setRawValue(newValue);
12678 preFocus : function(){
12680 if(this.selectOnFocus){
12681 this.inputEl().dom.select();
12684 filterKeys : function(e){
12685 var k = e.getKey();
12686 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12689 var c = e.getCharCode(), cc = String.fromCharCode(c);
12690 if(Roo.isIE && (e.isSpecialKey() || !cc)){
12693 if(!this.maskRe.test(cc)){
12698 * Clear any invalid styles/messages for this field
12700 clearInvalid : function(){
12702 if(!this.el || this.preventMark){ // not rendered
12707 this.el.removeClass([this.invalidClass, 'is-invalid']);
12709 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12711 var feedback = this.el.select('.form-control-feedback', true).first();
12714 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12719 if(this.indicator){
12720 this.indicator.removeClass('visible');
12721 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12724 this.fireEvent('valid', this);
12728 * Mark this field as valid
12730 markValid : function()
12732 if(!this.el || this.preventMark){ // not rendered...
12736 this.el.removeClass([this.invalidClass, this.validClass]);
12737 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12739 var feedback = this.el.select('.form-control-feedback', true).first();
12742 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12745 if(this.indicator){
12746 this.indicator.removeClass('visible');
12747 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12755 if(this.allowBlank && !this.getRawValue().length){
12758 if (Roo.bootstrap.version == 3) {
12759 this.el.addClass(this.validClass);
12761 this.inputEl().addClass('is-valid');
12764 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12766 var feedback = this.el.select('.form-control-feedback', true).first();
12769 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12770 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12775 this.fireEvent('valid', this);
12779 * Mark this field as invalid
12780 * @param {String} msg The validation message
12782 markInvalid : function(msg)
12784 if(!this.el || this.preventMark){ // not rendered
12788 this.el.removeClass([this.invalidClass, this.validClass]);
12789 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12791 var feedback = this.el.select('.form-control-feedback', true).first();
12794 this.el.select('.form-control-feedback', true).first().removeClass(
12795 [this.invalidFeedbackClass, this.validFeedbackClass]);
12802 if(this.allowBlank && !this.getRawValue().length){
12806 if(this.indicator){
12807 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12808 this.indicator.addClass('visible');
12810 if (Roo.bootstrap.version == 3) {
12811 this.el.addClass(this.invalidClass);
12813 this.inputEl().addClass('is-invalid');
12818 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12820 var feedback = this.el.select('.form-control-feedback', true).first();
12823 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12825 if(this.getValue().length || this.forceFeedback){
12826 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12833 this.fireEvent('invalid', this, msg);
12836 SafariOnKeyDown : function(event)
12838 // this is a workaround for a password hang bug on chrome/ webkit.
12839 if (this.inputEl().dom.type != 'password') {
12843 var isSelectAll = false;
12845 if(this.inputEl().dom.selectionEnd > 0){
12846 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12848 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12849 event.preventDefault();
12854 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12856 event.preventDefault();
12857 // this is very hacky as keydown always get's upper case.
12859 var cc = String.fromCharCode(event.getCharCode());
12860 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
12864 adjustWidth : function(tag, w){
12865 tag = tag.toLowerCase();
12866 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12867 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12868 if(tag == 'input'){
12871 if(tag == 'textarea'){
12874 }else if(Roo.isOpera){
12875 if(tag == 'input'){
12878 if(tag == 'textarea'){
12886 setFieldLabel : function(v)
12888 if(!this.rendered){
12892 if(this.indicatorEl()){
12893 var ar = this.el.select('label > span',true);
12895 if (ar.elements.length) {
12896 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12897 this.fieldLabel = v;
12901 var br = this.el.select('label',true);
12903 if(br.elements.length) {
12904 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12905 this.fieldLabel = v;
12909 Roo.log('Cannot Found any of label > span || label in input');
12913 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12914 this.fieldLabel = v;
12929 * @class Roo.bootstrap.TextArea
12930 * @extends Roo.bootstrap.Input
12931 * Bootstrap TextArea class
12932 * @cfg {Number} cols Specifies the visible width of a text area
12933 * @cfg {Number} rows Specifies the visible number of lines in a text area
12934 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12935 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12936 * @cfg {string} html text
12939 * Create a new TextArea
12940 * @param {Object} config The config object
12943 Roo.bootstrap.TextArea = function(config){
12944 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12948 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
12958 getAutoCreate : function(){
12960 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12966 if(this.inputType != 'hidden'){
12967 cfg.cls = 'form-group' //input-group
12975 value : this.value || '',
12976 html: this.html || '',
12977 cls : 'form-control',
12978 placeholder : this.placeholder || ''
12982 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12983 input.maxLength = this.maxLength;
12987 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12991 input.cols = this.cols;
12994 if (this.readOnly) {
12995 input.readonly = true;
12999 input.name = this.name;
13003 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13007 ['xs','sm','md','lg'].map(function(size){
13008 if (settings[size]) {
13009 cfg.cls += ' col-' + size + '-' + settings[size];
13013 var inputblock = input;
13015 if(this.hasFeedback && !this.allowBlank){
13019 cls: 'glyphicon form-control-feedback'
13023 cls : 'has-feedback',
13032 if (this.before || this.after) {
13035 cls : 'input-group',
13039 inputblock.cn.push({
13041 cls : 'input-group-addon',
13046 inputblock.cn.push(input);
13048 if(this.hasFeedback && !this.allowBlank){
13049 inputblock.cls += ' has-feedback';
13050 inputblock.cn.push(feedback);
13054 inputblock.cn.push({
13056 cls : 'input-group-addon',
13063 if (align ==='left' && this.fieldLabel.length) {
13068 cls : 'control-label',
13069 html : this.fieldLabel
13080 if(this.labelWidth > 12){
13081 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13084 if(this.labelWidth < 13 && this.labelmd == 0){
13085 this.labelmd = this.labelWidth;
13088 if(this.labellg > 0){
13089 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13090 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13093 if(this.labelmd > 0){
13094 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13095 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13098 if(this.labelsm > 0){
13099 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13100 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13103 if(this.labelxs > 0){
13104 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13105 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13108 } else if ( this.fieldLabel.length) {
13113 //cls : 'input-group-addon',
13114 html : this.fieldLabel
13132 if (this.disabled) {
13133 input.disabled=true;
13140 * return the real textarea element.
13142 inputEl: function ()
13144 return this.el.select('textarea.form-control',true).first();
13148 * Clear any invalid styles/messages for this field
13150 clearInvalid : function()
13153 if(!this.el || this.preventMark){ // not rendered
13157 var label = this.el.select('label', true).first();
13158 var icon = this.el.select('i.fa-star', true).first();
13163 this.el.removeClass( this.validClass);
13164 this.inputEl().removeClass('is-invalid');
13166 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13168 var feedback = this.el.select('.form-control-feedback', true).first();
13171 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13176 this.fireEvent('valid', this);
13180 * Mark this field as valid
13182 markValid : function()
13184 if(!this.el || this.preventMark){ // not rendered
13188 this.el.removeClass([this.invalidClass, this.validClass]);
13189 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13191 var feedback = this.el.select('.form-control-feedback', true).first();
13194 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13197 if(this.disabled || this.allowBlank){
13201 var label = this.el.select('label', true).first();
13202 var icon = this.el.select('i.fa-star', true).first();
13207 if (Roo.bootstrap.version == 3) {
13208 this.el.addClass(this.validClass);
13210 this.inputEl().addClass('is-valid');
13214 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13216 var feedback = this.el.select('.form-control-feedback', true).first();
13219 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13220 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13225 this.fireEvent('valid', this);
13229 * Mark this field as invalid
13230 * @param {String} msg The validation message
13232 markInvalid : function(msg)
13234 if(!this.el || this.preventMark){ // not rendered
13238 this.el.removeClass([this.invalidClass, this.validClass]);
13239 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13241 var feedback = this.el.select('.form-control-feedback', true).first();
13244 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13247 if(this.disabled || this.allowBlank){
13251 var label = this.el.select('label', true).first();
13252 var icon = this.el.select('i.fa-star', true).first();
13254 if(!this.getValue().length && label && !icon){
13255 this.el.createChild({
13257 cls : 'text-danger fa fa-lg fa-star',
13258 tooltip : 'This field is required',
13259 style : 'margin-right:5px;'
13263 if (Roo.bootstrap.version == 3) {
13264 this.el.addClass(this.invalidClass);
13266 this.inputEl().addClass('is-invalid');
13269 // fixme ... this may be depricated need to test..
13270 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13272 var feedback = this.el.select('.form-control-feedback', true).first();
13275 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13277 if(this.getValue().length || this.forceFeedback){
13278 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13285 this.fireEvent('invalid', this, msg);
13293 * trigger field - base class for combo..
13298 * @class Roo.bootstrap.TriggerField
13299 * @extends Roo.bootstrap.Input
13300 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13301 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13302 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13303 * for which you can provide a custom implementation. For example:
13305 var trigger = new Roo.bootstrap.TriggerField();
13306 trigger.onTriggerClick = myTriggerFn;
13307 trigger.applyTo('my-field');
13310 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13311 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13312 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13313 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13314 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13317 * Create a new TriggerField.
13318 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13319 * to the base TextField)
13321 Roo.bootstrap.TriggerField = function(config){
13322 this.mimicing = false;
13323 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13326 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
13328 * @cfg {String} triggerClass A CSS class to apply to the trigger
13331 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13336 * @cfg {Boolean} removable (true|false) special filter default false
13340 /** @cfg {Boolean} grow @hide */
13341 /** @cfg {Number} growMin @hide */
13342 /** @cfg {Number} growMax @hide */
13348 autoSize: Roo.emptyFn,
13352 deferHeight : true,
13355 actionMode : 'wrap',
13360 getAutoCreate : function(){
13362 var align = this.labelAlign || this.parentLabelAlign();
13367 cls: 'form-group' //input-group
13374 type : this.inputType,
13375 cls : 'form-control',
13376 autocomplete: 'new-password',
13377 placeholder : this.placeholder || ''
13381 input.name = this.name;
13384 input.cls += ' input-' + this.size;
13387 if (this.disabled) {
13388 input.disabled=true;
13391 var inputblock = input;
13393 if(this.hasFeedback && !this.allowBlank){
13397 cls: 'glyphicon form-control-feedback'
13400 if(this.removable && !this.editable ){
13402 cls : 'has-feedback',
13408 cls : 'roo-combo-removable-btn close'
13415 cls : 'has-feedback',
13424 if(this.removable && !this.editable ){
13426 cls : 'roo-removable',
13432 cls : 'roo-combo-removable-btn close'
13439 if (this.before || this.after) {
13442 cls : 'input-group',
13446 inputblock.cn.push({
13448 cls : 'input-group-addon input-group-prepend input-group-text',
13453 inputblock.cn.push(input);
13455 if(this.hasFeedback && !this.allowBlank){
13456 inputblock.cls += ' has-feedback';
13457 inputblock.cn.push(feedback);
13461 inputblock.cn.push({
13463 cls : 'input-group-addon input-group-append input-group-text',
13472 var ibwrap = inputblock;
13477 cls: 'roo-select2-choices',
13481 cls: 'roo-select2-search-field',
13493 cls: 'roo-select2-container input-group',
13498 cls: 'form-hidden-field'
13504 if(!this.multiple && this.showToggleBtn){
13510 if (this.caret != false) {
13513 cls: 'fa fa-' + this.caret
13520 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13522 Roo.bootstrap.version == 3 ? caret : '',
13525 cls: 'combobox-clear',
13539 combobox.cls += ' roo-select2-container-multi';
13543 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13544 tooltip : 'This field is required'
13546 if (Roo.bootstrap.version == 4) {
13549 style : 'display:none'
13554 if (align ==='left' && this.fieldLabel.length) {
13556 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13563 cls : 'control-label',
13564 html : this.fieldLabel
13576 var labelCfg = cfg.cn[1];
13577 var contentCfg = cfg.cn[2];
13579 if(this.indicatorpos == 'right'){
13584 cls : 'control-label',
13588 html : this.fieldLabel
13602 labelCfg = cfg.cn[0];
13603 contentCfg = cfg.cn[1];
13606 if(this.labelWidth > 12){
13607 labelCfg.style = "width: " + this.labelWidth + 'px';
13610 if(this.labelWidth < 13 && this.labelmd == 0){
13611 this.labelmd = this.labelWidth;
13614 if(this.labellg > 0){
13615 labelCfg.cls += ' col-lg-' + this.labellg;
13616 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13619 if(this.labelmd > 0){
13620 labelCfg.cls += ' col-md-' + this.labelmd;
13621 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13624 if(this.labelsm > 0){
13625 labelCfg.cls += ' col-sm-' + this.labelsm;
13626 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13629 if(this.labelxs > 0){
13630 labelCfg.cls += ' col-xs-' + this.labelxs;
13631 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13634 } else if ( this.fieldLabel.length) {
13635 // Roo.log(" label");
13640 //cls : 'input-group-addon',
13641 html : this.fieldLabel
13649 if(this.indicatorpos == 'right'){
13657 html : this.fieldLabel
13671 // Roo.log(" no label && no align");
13678 ['xs','sm','md','lg'].map(function(size){
13679 if (settings[size]) {
13680 cfg.cls += ' col-' + size + '-' + settings[size];
13691 onResize : function(w, h){
13692 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13693 // if(typeof w == 'number'){
13694 // var x = w - this.trigger.getWidth();
13695 // this.inputEl().setWidth(this.adjustWidth('input', x));
13696 // this.trigger.setStyle('left', x+'px');
13701 adjustSize : Roo.BoxComponent.prototype.adjustSize,
13704 getResizeEl : function(){
13705 return this.inputEl();
13709 getPositionEl : function(){
13710 return this.inputEl();
13714 alignErrorIcon : function(){
13715 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13719 initEvents : function(){
13723 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13724 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13725 if(!this.multiple && this.showToggleBtn){
13726 this.trigger = this.el.select('span.dropdown-toggle',true).first();
13727 if(this.hideTrigger){
13728 this.trigger.setDisplayed(false);
13730 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13734 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13737 if(this.removable && !this.editable && !this.tickable){
13738 var close = this.closeTriggerEl();
13741 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13742 close.on('click', this.removeBtnClick, this, close);
13746 //this.trigger.addClassOnOver('x-form-trigger-over');
13747 //this.trigger.addClassOnClick('x-form-trigger-click');
13750 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13754 closeTriggerEl : function()
13756 var close = this.el.select('.roo-combo-removable-btn', true).first();
13757 return close ? close : false;
13760 removeBtnClick : function(e, h, el)
13762 e.preventDefault();
13764 if(this.fireEvent("remove", this) !== false){
13766 this.fireEvent("afterremove", this)
13770 createList : function()
13772 this.list = Roo.get(document.body).createChild({
13773 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13774 cls: 'typeahead typeahead-long dropdown-menu shadow',
13775 style: 'display:none'
13778 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13783 initTrigger : function(){
13788 onDestroy : function(){
13790 this.trigger.removeAllListeners();
13791 // this.trigger.remove();
13794 // this.wrap.remove();
13796 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13800 onFocus : function(){
13801 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13803 if(!this.mimicing){
13804 this.wrap.addClass('x-trigger-wrap-focus');
13805 this.mimicing = true;
13806 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13807 if(this.monitorTab){
13808 this.el.on("keydown", this.checkTab, this);
13815 checkTab : function(e){
13816 if(e.getKey() == e.TAB){
13817 this.triggerBlur();
13822 onBlur : function(){
13827 mimicBlur : function(e, t){
13829 if(!this.wrap.contains(t) && this.validateBlur()){
13830 this.triggerBlur();
13836 triggerBlur : function(){
13837 this.mimicing = false;
13838 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13839 if(this.monitorTab){
13840 this.el.un("keydown", this.checkTab, this);
13842 //this.wrap.removeClass('x-trigger-wrap-focus');
13843 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13847 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13848 validateBlur : function(e, t){
13853 onDisable : function(){
13854 this.inputEl().dom.disabled = true;
13855 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13857 // this.wrap.addClass('x-item-disabled');
13862 onEnable : function(){
13863 this.inputEl().dom.disabled = false;
13864 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13866 // this.el.removeClass('x-item-disabled');
13871 onShow : function(){
13872 var ae = this.getActionEl();
13875 ae.dom.style.display = '';
13876 ae.dom.style.visibility = 'visible';
13882 onHide : function(){
13883 var ae = this.getActionEl();
13884 ae.dom.style.display = 'none';
13888 * The function that should handle the trigger's click event. This method does nothing by default until overridden
13889 * by an implementing function.
13891 * @param {EventObject} e
13893 onTriggerClick : Roo.emptyFn
13901 * @class Roo.bootstrap.CardUploader
13902 * @extends Roo.bootstrap.Button
13903 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13904 * @cfg {Number} errorTimeout default 3000
13905 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
13906 * @cfg {Array} html The button text.
13910 * Create a new CardUploader
13911 * @param {Object} config The config object
13914 Roo.bootstrap.CardUploader = function(config){
13918 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13921 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
13929 * When a image is clicked on - and needs to display a slideshow or similar..
13930 * @param {Roo.bootstrap.Card} this
13931 * @param {Object} The image information data
13937 * When a the download link is clicked
13938 * @param {Roo.bootstrap.Card} this
13939 * @param {Object} The image information data contains
13946 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
13949 errorTimeout : 3000,
13953 fileCollection : false,
13956 getAutoCreate : function()
13960 cls :'form-group' ,
13965 //cls : 'input-group-addon',
13966 html : this.fieldLabel
13974 value : this.value,
13975 cls : 'd-none form-control'
13980 multiple : 'multiple',
13982 cls : 'd-none roo-card-upload-selector'
13986 cls : 'roo-card-uploader-button-container w-100 mb-2'
13989 cls : 'card-columns roo-card-uploader-container'
13999 getChildContainer : function() /// what children are added to.
14001 return this.containerEl;
14004 getButtonContainer : function() /// what children are added to.
14006 return this.el.select(".roo-card-uploader-button-container").first();
14009 initEvents : function()
14012 Roo.bootstrap.Input.prototype.initEvents.call(this);
14016 xns: Roo.bootstrap,
14019 container_method : 'getButtonContainer' ,
14020 html : this.html, // fix changable?
14023 'click' : function(btn, e) {
14032 this.urlAPI = (window.createObjectURL && window) ||
14033 (window.URL && URL.revokeObjectURL && URL) ||
14034 (window.webkitURL && webkitURL);
14039 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14041 this.selectorEl.on('change', this.onFileSelected, this);
14044 this.images.forEach(function(img) {
14047 this.images = false;
14049 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14055 onClick : function(e)
14057 e.preventDefault();
14059 this.selectorEl.dom.click();
14063 onFileSelected : function(e)
14065 e.preventDefault();
14067 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14071 Roo.each(this.selectorEl.dom.files, function(file){
14072 this.addFile(file);
14081 addFile : function(file)
14084 if(typeof(file) === 'string'){
14085 throw "Add file by name?"; // should not happen
14089 if(!file || !this.urlAPI){
14099 var url = _this.urlAPI.createObjectURL( file);
14102 id : Roo.bootstrap.CardUploader.ID--,
14103 is_uploaded : false,
14107 mimetype : file.type,
14115 * addCard - add an Attachment to the uploader
14116 * @param data - the data about the image to upload
14120 title : "Title of file",
14121 is_uploaded : false,
14122 src : "http://.....",
14123 srcfile : { the File upload object },
14124 mimetype : file.type,
14127 .. any other data...
14133 addCard : function (data)
14135 // hidden input element?
14136 // if the file is not an image...
14137 //then we need to use something other that and header_image
14142 xns : Roo.bootstrap,
14143 xtype : 'CardFooter',
14146 xns : Roo.bootstrap,
14152 xns : Roo.bootstrap,
14154 html : String.format("<small>{0}</small>", data.title),
14155 cls : 'col-10 text-left',
14160 click : function() {
14162 t.fireEvent( "download", t, data );
14168 xns : Roo.bootstrap,
14170 style: 'max-height: 28px; ',
14176 click : function() {
14177 t.removeCard(data.id)
14189 var cn = this.addxtype(
14192 xns : Roo.bootstrap,
14195 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14196 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14197 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14202 initEvents : function() {
14203 Roo.bootstrap.Card.prototype.initEvents.call(this);
14205 this.imgEl = this.el.select('.card-img-top').first();
14207 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14208 this.imgEl.set({ 'pointer' : 'cursor' });
14211 this.getCardFooter().addClass('p-1');
14218 // dont' really need ot update items.
14219 // this.items.push(cn);
14220 this.fileCollection.add(cn);
14222 if (!data.srcfile) {
14223 this.updateInput();
14228 var reader = new FileReader();
14229 reader.addEventListener("load", function() {
14230 data.srcdata = reader.result;
14233 reader.readAsDataURL(data.srcfile);
14238 removeCard : function(id)
14241 var card = this.fileCollection.get(id);
14242 card.data.is_deleted = 1;
14243 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14244 //this.fileCollection.remove(card);
14245 //this.items = this.items.filter(function(e) { return e != card });
14246 // dont' really need ot update items.
14247 card.el.dom.parentNode.removeChild(card.el.dom);
14248 this.updateInput();
14254 this.fileCollection.each(function(card) {
14255 if (card.el.dom && card.el.dom.parentNode) {
14256 card.el.dom.parentNode.removeChild(card.el.dom);
14259 this.fileCollection.clear();
14260 this.updateInput();
14263 updateInput : function()
14266 this.fileCollection.each(function(e) {
14270 this.inputEl().dom.value = JSON.stringify(data);
14280 Roo.bootstrap.CardUploader.ID = -1;/*
14282 * Ext JS Library 1.1.1
14283 * Copyright(c) 2006-2007, Ext JS, LLC.
14285 * Originally Released Under LGPL - original licence link has changed is not relivant.
14288 * <script type="text/javascript">
14293 * @class Roo.data.SortTypes
14295 * Defines the default sorting (casting?) comparison functions used when sorting data.
14297 Roo.data.SortTypes = {
14299 * Default sort that does nothing
14300 * @param {Mixed} s The value being converted
14301 * @return {Mixed} The comparison value
14303 none : function(s){
14308 * The regular expression used to strip tags
14312 stripTagsRE : /<\/?[^>]+>/gi,
14315 * Strips all HTML tags to sort on text only
14316 * @param {Mixed} s The value being converted
14317 * @return {String} The comparison value
14319 asText : function(s){
14320 return String(s).replace(this.stripTagsRE, "");
14324 * Strips all HTML tags to sort on text only - Case insensitive
14325 * @param {Mixed} s The value being converted
14326 * @return {String} The comparison value
14328 asUCText : function(s){
14329 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14333 * Case insensitive string
14334 * @param {Mixed} s The value being converted
14335 * @return {String} The comparison value
14337 asUCString : function(s) {
14338 return String(s).toUpperCase();
14343 * @param {Mixed} s The value being converted
14344 * @return {Number} The comparison value
14346 asDate : function(s) {
14350 if(s instanceof Date){
14351 return s.getTime();
14353 return Date.parse(String(s));
14358 * @param {Mixed} s The value being converted
14359 * @return {Float} The comparison value
14361 asFloat : function(s) {
14362 var val = parseFloat(String(s).replace(/,/g, ""));
14371 * @param {Mixed} s The value being converted
14372 * @return {Number} The comparison value
14374 asInt : function(s) {
14375 var val = parseInt(String(s).replace(/,/g, ""));
14383 * Ext JS Library 1.1.1
14384 * Copyright(c) 2006-2007, Ext JS, LLC.
14386 * Originally Released Under LGPL - original licence link has changed is not relivant.
14389 * <script type="text/javascript">
14393 * @class Roo.data.Record
14394 * Instances of this class encapsulate both record <em>definition</em> information, and record
14395 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14396 * to access Records cached in an {@link Roo.data.Store} object.<br>
14398 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14399 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14402 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14404 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14405 * {@link #create}. The parameters are the same.
14406 * @param {Array} data An associative Array of data values keyed by the field name.
14407 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14408 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14409 * not specified an integer id is generated.
14411 Roo.data.Record = function(data, id){
14412 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14417 * Generate a constructor for a specific record layout.
14418 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14419 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14420 * Each field definition object may contain the following properties: <ul>
14421 * <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,
14422 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14423 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14424 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14425 * is being used, then this is a string containing the javascript expression to reference the data relative to
14426 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14427 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14428 * this may be omitted.</p></li>
14429 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14430 * <ul><li>auto (Default, implies no conversion)</li>
14435 * <li>date</li></ul></p></li>
14436 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14437 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14438 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14439 * by the Reader into an object that will be stored in the Record. It is passed the
14440 * following parameters:<ul>
14441 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14443 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14445 * <br>usage:<br><pre><code>
14446 var TopicRecord = Roo.data.Record.create(
14447 {name: 'title', mapping: 'topic_title'},
14448 {name: 'author', mapping: 'username'},
14449 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14450 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14451 {name: 'lastPoster', mapping: 'user2'},
14452 {name: 'excerpt', mapping: 'post_text'}
14455 var myNewRecord = new TopicRecord({
14456 title: 'Do my job please',
14459 lastPost: new Date(),
14460 lastPoster: 'Animal',
14461 excerpt: 'No way dude!'
14463 myStore.add(myNewRecord);
14468 Roo.data.Record.create = function(o){
14469 var f = function(){
14470 f.superclass.constructor.apply(this, arguments);
14472 Roo.extend(f, Roo.data.Record);
14473 var p = f.prototype;
14474 p.fields = new Roo.util.MixedCollection(false, function(field){
14477 for(var i = 0, len = o.length; i < len; i++){
14478 p.fields.add(new Roo.data.Field(o[i]));
14480 f.getField = function(name){
14481 return p.fields.get(name);
14486 Roo.data.Record.AUTO_ID = 1000;
14487 Roo.data.Record.EDIT = 'edit';
14488 Roo.data.Record.REJECT = 'reject';
14489 Roo.data.Record.COMMIT = 'commit';
14491 Roo.data.Record.prototype = {
14493 * Readonly flag - true if this record has been modified.
14502 join : function(store){
14503 this.store = store;
14507 * Set the named field to the specified value.
14508 * @param {String} name The name of the field to set.
14509 * @param {Object} value The value to set the field to.
14511 set : function(name, value){
14512 if(this.data[name] == value){
14516 if(!this.modified){
14517 this.modified = {};
14519 if(typeof this.modified[name] == 'undefined'){
14520 this.modified[name] = this.data[name];
14522 this.data[name] = value;
14523 if(!this.editing && this.store){
14524 this.store.afterEdit(this);
14529 * Get the value of the named field.
14530 * @param {String} name The name of the field to get the value of.
14531 * @return {Object} The value of the field.
14533 get : function(name){
14534 return this.data[name];
14538 beginEdit : function(){
14539 this.editing = true;
14540 this.modified = {};
14544 cancelEdit : function(){
14545 this.editing = false;
14546 delete this.modified;
14550 endEdit : function(){
14551 this.editing = false;
14552 if(this.dirty && this.store){
14553 this.store.afterEdit(this);
14558 * Usually called by the {@link Roo.data.Store} which owns the Record.
14559 * Rejects all changes made to the Record since either creation, or the last commit operation.
14560 * Modified fields are reverted to their original values.
14562 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14563 * of reject operations.
14565 reject : function(){
14566 var m = this.modified;
14568 if(typeof m[n] != "function"){
14569 this.data[n] = m[n];
14572 this.dirty = false;
14573 delete this.modified;
14574 this.editing = false;
14576 this.store.afterReject(this);
14581 * Usually called by the {@link Roo.data.Store} which owns the Record.
14582 * Commits all changes made to the Record since either creation, or the last commit operation.
14584 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14585 * of commit operations.
14587 commit : function(){
14588 this.dirty = false;
14589 delete this.modified;
14590 this.editing = false;
14592 this.store.afterCommit(this);
14597 hasError : function(){
14598 return this.error != null;
14602 clearError : function(){
14607 * Creates a copy of this record.
14608 * @param {String} id (optional) A new record id if you don't want to use this record's id
14611 copy : function(newId) {
14612 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14616 * Ext JS Library 1.1.1
14617 * Copyright(c) 2006-2007, Ext JS, LLC.
14619 * Originally Released Under LGPL - original licence link has changed is not relivant.
14622 * <script type="text/javascript">
14628 * @class Roo.data.Store
14629 * @extends Roo.util.Observable
14630 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14631 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14633 * 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
14634 * has no knowledge of the format of the data returned by the Proxy.<br>
14636 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14637 * instances from the data object. These records are cached and made available through accessor functions.
14639 * Creates a new Store.
14640 * @param {Object} config A config object containing the objects needed for the Store to access data,
14641 * and read the data into Records.
14643 Roo.data.Store = function(config){
14644 this.data = new Roo.util.MixedCollection(false);
14645 this.data.getKey = function(o){
14648 this.baseParams = {};
14650 this.paramNames = {
14655 "multisort" : "_multisort"
14658 if(config && config.data){
14659 this.inlineData = config.data;
14660 delete config.data;
14663 Roo.apply(this, config);
14665 if(this.reader){ // reader passed
14666 this.reader = Roo.factory(this.reader, Roo.data);
14667 this.reader.xmodule = this.xmodule || false;
14668 if(!this.recordType){
14669 this.recordType = this.reader.recordType;
14671 if(this.reader.onMetaChange){
14672 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14676 if(this.recordType){
14677 this.fields = this.recordType.prototype.fields;
14679 this.modified = [];
14683 * @event datachanged
14684 * Fires when the data cache has changed, and a widget which is using this Store
14685 * as a Record cache should refresh its view.
14686 * @param {Store} this
14688 datachanged : true,
14690 * @event metachange
14691 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14692 * @param {Store} this
14693 * @param {Object} meta The JSON metadata
14698 * Fires when Records have been added to the Store
14699 * @param {Store} this
14700 * @param {Roo.data.Record[]} records The array of Records added
14701 * @param {Number} index The index at which the record(s) were added
14706 * Fires when a Record has been removed from the Store
14707 * @param {Store} this
14708 * @param {Roo.data.Record} record The Record that was removed
14709 * @param {Number} index The index at which the record was removed
14714 * Fires when a Record has been updated
14715 * @param {Store} this
14716 * @param {Roo.data.Record} record The Record that was updated
14717 * @param {String} operation The update operation being performed. Value may be one of:
14719 Roo.data.Record.EDIT
14720 Roo.data.Record.REJECT
14721 Roo.data.Record.COMMIT
14727 * Fires when the data cache has been cleared.
14728 * @param {Store} this
14732 * @event beforeload
14733 * Fires before a request is made for a new data object. If the beforeload handler returns false
14734 * the load action will be canceled.
14735 * @param {Store} this
14736 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14740 * @event beforeloadadd
14741 * Fires after a new set of Records has been loaded.
14742 * @param {Store} this
14743 * @param {Roo.data.Record[]} records The Records that were loaded
14744 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14746 beforeloadadd : true,
14749 * Fires after a new set of Records has been loaded, before they are added to the store.
14750 * @param {Store} this
14751 * @param {Roo.data.Record[]} records The Records that were loaded
14752 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14753 * @params {Object} return from reader
14757 * @event loadexception
14758 * Fires if an exception occurs in the Proxy during loading.
14759 * Called with the signature of the Proxy's "loadexception" event.
14760 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14763 * @param {Object} return from JsonData.reader() - success, totalRecords, records
14764 * @param {Object} load options
14765 * @param {Object} jsonData from your request (normally this contains the Exception)
14767 loadexception : true
14771 this.proxy = Roo.factory(this.proxy, Roo.data);
14772 this.proxy.xmodule = this.xmodule || false;
14773 this.relayEvents(this.proxy, ["loadexception"]);
14775 this.sortToggle = {};
14776 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14778 Roo.data.Store.superclass.constructor.call(this);
14780 if(this.inlineData){
14781 this.loadData(this.inlineData);
14782 delete this.inlineData;
14786 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14788 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
14789 * without a remote query - used by combo/forms at present.
14793 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
14796 * @cfg {Array} data Inline data to be loaded when the store is initialized.
14799 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
14800 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14803 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14804 * on any HTTP request
14807 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14810 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14814 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14815 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14817 remoteSort : false,
14820 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14821 * loaded or when a record is removed. (defaults to false).
14823 pruneModifiedRecords : false,
14826 lastOptions : null,
14829 * Add Records to the Store and fires the add event.
14830 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14832 add : function(records){
14833 records = [].concat(records);
14834 for(var i = 0, len = records.length; i < len; i++){
14835 records[i].join(this);
14837 var index = this.data.length;
14838 this.data.addAll(records);
14839 this.fireEvent("add", this, records, index);
14843 * Remove a Record from the Store and fires the remove event.
14844 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14846 remove : function(record){
14847 var index = this.data.indexOf(record);
14848 this.data.removeAt(index);
14850 if(this.pruneModifiedRecords){
14851 this.modified.remove(record);
14853 this.fireEvent("remove", this, record, index);
14857 * Remove all Records from the Store and fires the clear event.
14859 removeAll : function(){
14861 if(this.pruneModifiedRecords){
14862 this.modified = [];
14864 this.fireEvent("clear", this);
14868 * Inserts Records to the Store at the given index and fires the add event.
14869 * @param {Number} index The start index at which to insert the passed Records.
14870 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14872 insert : function(index, records){
14873 records = [].concat(records);
14874 for(var i = 0, len = records.length; i < len; i++){
14875 this.data.insert(index, records[i]);
14876 records[i].join(this);
14878 this.fireEvent("add", this, records, index);
14882 * Get the index within the cache of the passed Record.
14883 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14884 * @return {Number} The index of the passed Record. Returns -1 if not found.
14886 indexOf : function(record){
14887 return this.data.indexOf(record);
14891 * Get the index within the cache of the Record with the passed id.
14892 * @param {String} id The id of the Record to find.
14893 * @return {Number} The index of the Record. Returns -1 if not found.
14895 indexOfId : function(id){
14896 return this.data.indexOfKey(id);
14900 * Get the Record with the specified id.
14901 * @param {String} id The id of the Record to find.
14902 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14904 getById : function(id){
14905 return this.data.key(id);
14909 * Get the Record at the specified index.
14910 * @param {Number} index The index of the Record to find.
14911 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14913 getAt : function(index){
14914 return this.data.itemAt(index);
14918 * Returns a range of Records between specified indices.
14919 * @param {Number} startIndex (optional) The starting index (defaults to 0)
14920 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14921 * @return {Roo.data.Record[]} An array of Records
14923 getRange : function(start, end){
14924 return this.data.getRange(start, end);
14928 storeOptions : function(o){
14929 o = Roo.apply({}, o);
14932 this.lastOptions = o;
14936 * Loads the Record cache from the configured Proxy using the configured Reader.
14938 * If using remote paging, then the first load call must specify the <em>start</em>
14939 * and <em>limit</em> properties in the options.params property to establish the initial
14940 * position within the dataset, and the number of Records to cache on each read from the Proxy.
14942 * <strong>It is important to note that for remote data sources, loading is asynchronous,
14943 * and this call will return before the new data has been loaded. Perform any post-processing
14944 * in a callback function, or in a "load" event handler.</strong>
14946 * @param {Object} options An object containing properties which control loading options:<ul>
14947 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14948 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14949 * passed the following arguments:<ul>
14950 * <li>r : Roo.data.Record[]</li>
14951 * <li>options: Options object from the load call</li>
14952 * <li>success: Boolean success indicator</li></ul></li>
14953 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14954 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14957 load : function(options){
14958 options = options || {};
14959 if(this.fireEvent("beforeload", this, options) !== false){
14960 this.storeOptions(options);
14961 var p = Roo.apply(options.params || {}, this.baseParams);
14962 // if meta was not loaded from remote source.. try requesting it.
14963 if (!this.reader.metaFromRemote) {
14964 p._requestMeta = 1;
14966 if(this.sortInfo && this.remoteSort){
14967 var pn = this.paramNames;
14968 p[pn["sort"]] = this.sortInfo.field;
14969 p[pn["dir"]] = this.sortInfo.direction;
14971 if (this.multiSort) {
14972 var pn = this.paramNames;
14973 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14976 this.proxy.load(p, this.reader, this.loadRecords, this, options);
14981 * Reloads the Record cache from the configured Proxy using the configured Reader and
14982 * the options from the last load operation performed.
14983 * @param {Object} options (optional) An object containing properties which may override the options
14984 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14985 * the most recently used options are reused).
14987 reload : function(options){
14988 this.load(Roo.applyIf(options||{}, this.lastOptions));
14992 // Called as a callback by the Reader during a load operation.
14993 loadRecords : function(o, options, success){
14994 if(!o || success === false){
14995 if(success !== false){
14996 this.fireEvent("load", this, [], options, o);
14998 if(options.callback){
14999 options.callback.call(options.scope || this, [], options, false);
15003 // if data returned failure - throw an exception.
15004 if (o.success === false) {
15005 // show a message if no listener is registered.
15006 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15007 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15009 // loadmask wil be hooked into this..
15010 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15013 var r = o.records, t = o.totalRecords || r.length;
15015 this.fireEvent("beforeloadadd", this, r, options, o);
15017 if(!options || options.add !== true){
15018 if(this.pruneModifiedRecords){
15019 this.modified = [];
15021 for(var i = 0, len = r.length; i < len; i++){
15025 this.data = this.snapshot;
15026 delete this.snapshot;
15029 this.data.addAll(r);
15030 this.totalLength = t;
15032 this.fireEvent("datachanged", this);
15034 this.totalLength = Math.max(t, this.data.length+r.length);
15038 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15040 var e = new Roo.data.Record({});
15042 e.set(this.parent.displayField, this.parent.emptyTitle);
15043 e.set(this.parent.valueField, '');
15048 this.fireEvent("load", this, r, options, o);
15049 if(options.callback){
15050 options.callback.call(options.scope || this, r, options, true);
15056 * Loads data from a passed data block. A Reader which understands the format of the data
15057 * must have been configured in the constructor.
15058 * @param {Object} data The data block from which to read the Records. The format of the data expected
15059 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15060 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15062 loadData : function(o, append){
15063 var r = this.reader.readRecords(o);
15064 this.loadRecords(r, {add: append}, true);
15068 * using 'cn' the nested child reader read the child array into it's child stores.
15069 * @param {Object} rec The record with a 'children array
15071 loadDataFromChildren : function(rec)
15073 this.loadData(this.reader.toLoadData(rec));
15078 * Gets the number of cached records.
15080 * <em>If using paging, this may not be the total size of the dataset. If the data object
15081 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15082 * the data set size</em>
15084 getCount : function(){
15085 return this.data.length || 0;
15089 * Gets the total number of records in the dataset as returned by the server.
15091 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15092 * the dataset size</em>
15094 getTotalCount : function(){
15095 return this.totalLength || 0;
15099 * Returns the sort state of the Store as an object with two properties:
15101 field {String} The name of the field by which the Records are sorted
15102 direction {String} The sort order, "ASC" or "DESC"
15105 getSortState : function(){
15106 return this.sortInfo;
15110 applySort : function(){
15111 if(this.sortInfo && !this.remoteSort){
15112 var s = this.sortInfo, f = s.field;
15113 var st = this.fields.get(f).sortType;
15114 var fn = function(r1, r2){
15115 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15116 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15118 this.data.sort(s.direction, fn);
15119 if(this.snapshot && this.snapshot != this.data){
15120 this.snapshot.sort(s.direction, fn);
15126 * Sets the default sort column and order to be used by the next load operation.
15127 * @param {String} fieldName The name of the field to sort by.
15128 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15130 setDefaultSort : function(field, dir){
15131 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15135 * Sort the Records.
15136 * If remote sorting is used, the sort is performed on the server, and the cache is
15137 * reloaded. If local sorting is used, the cache is sorted internally.
15138 * @param {String} fieldName The name of the field to sort by.
15139 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15141 sort : function(fieldName, dir){
15142 var f = this.fields.get(fieldName);
15144 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15146 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15147 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15152 this.sortToggle[f.name] = dir;
15153 this.sortInfo = {field: f.name, direction: dir};
15154 if(!this.remoteSort){
15156 this.fireEvent("datachanged", this);
15158 this.load(this.lastOptions);
15163 * Calls the specified function for each of the Records in the cache.
15164 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15165 * Returning <em>false</em> aborts and exits the iteration.
15166 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15168 each : function(fn, scope){
15169 this.data.each(fn, scope);
15173 * Gets all records modified since the last commit. Modified records are persisted across load operations
15174 * (e.g., during paging).
15175 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15177 getModifiedRecords : function(){
15178 return this.modified;
15182 createFilterFn : function(property, value, anyMatch){
15183 if(!value.exec){ // not a regex
15184 value = String(value);
15185 if(value.length == 0){
15188 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15190 return function(r){
15191 return value.test(r.data[property]);
15196 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15197 * @param {String} property A field on your records
15198 * @param {Number} start The record index to start at (defaults to 0)
15199 * @param {Number} end The last record index to include (defaults to length - 1)
15200 * @return {Number} The sum
15202 sum : function(property, start, end){
15203 var rs = this.data.items, v = 0;
15204 start = start || 0;
15205 end = (end || end === 0) ? end : rs.length-1;
15207 for(var i = start; i <= end; i++){
15208 v += (rs[i].data[property] || 0);
15214 * Filter the records by a specified property.
15215 * @param {String} field A field on your records
15216 * @param {String/RegExp} value Either a string that the field
15217 * should start with or a RegExp to test against the field
15218 * @param {Boolean} anyMatch True to match any part not just the beginning
15220 filter : function(property, value, anyMatch){
15221 var fn = this.createFilterFn(property, value, anyMatch);
15222 return fn ? this.filterBy(fn) : this.clearFilter();
15226 * Filter by a function. The specified function will be called with each
15227 * record in this data source. If the function returns true the record is included,
15228 * otherwise it is filtered.
15229 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15230 * @param {Object} scope (optional) The scope of the function (defaults to this)
15232 filterBy : function(fn, scope){
15233 this.snapshot = this.snapshot || this.data;
15234 this.data = this.queryBy(fn, scope||this);
15235 this.fireEvent("datachanged", this);
15239 * Query the records by a specified property.
15240 * @param {String} field A field on your records
15241 * @param {String/RegExp} value Either a string that the field
15242 * should start with or a RegExp to test against the field
15243 * @param {Boolean} anyMatch True to match any part not just the beginning
15244 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15246 query : function(property, value, anyMatch){
15247 var fn = this.createFilterFn(property, value, anyMatch);
15248 return fn ? this.queryBy(fn) : this.data.clone();
15252 * Query by a function. The specified function will be called with each
15253 * record in this data source. If the function returns true the record is included
15255 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15256 * @param {Object} scope (optional) The scope of the function (defaults to this)
15257 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15259 queryBy : function(fn, scope){
15260 var data = this.snapshot || this.data;
15261 return data.filterBy(fn, scope||this);
15265 * Collects unique values for a particular dataIndex from this store.
15266 * @param {String} dataIndex The property to collect
15267 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15268 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15269 * @return {Array} An array of the unique values
15271 collect : function(dataIndex, allowNull, bypassFilter){
15272 var d = (bypassFilter === true && this.snapshot) ?
15273 this.snapshot.items : this.data.items;
15274 var v, sv, r = [], l = {};
15275 for(var i = 0, len = d.length; i < len; i++){
15276 v = d[i].data[dataIndex];
15278 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15287 * Revert to a view of the Record cache with no filtering applied.
15288 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15290 clearFilter : function(suppressEvent){
15291 if(this.snapshot && this.snapshot != this.data){
15292 this.data = this.snapshot;
15293 delete this.snapshot;
15294 if(suppressEvent !== true){
15295 this.fireEvent("datachanged", this);
15301 afterEdit : function(record){
15302 if(this.modified.indexOf(record) == -1){
15303 this.modified.push(record);
15305 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15309 afterReject : function(record){
15310 this.modified.remove(record);
15311 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15315 afterCommit : function(record){
15316 this.modified.remove(record);
15317 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15321 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15322 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15324 commitChanges : function(){
15325 var m = this.modified.slice(0);
15326 this.modified = [];
15327 for(var i = 0, len = m.length; i < len; i++){
15333 * Cancel outstanding changes on all changed records.
15335 rejectChanges : function(){
15336 var m = this.modified.slice(0);
15337 this.modified = [];
15338 for(var i = 0, len = m.length; i < len; i++){
15343 onMetaChange : function(meta, rtype, o){
15344 this.recordType = rtype;
15345 this.fields = rtype.prototype.fields;
15346 delete this.snapshot;
15347 this.sortInfo = meta.sortInfo || this.sortInfo;
15348 this.modified = [];
15349 this.fireEvent('metachange', this, this.reader.meta);
15352 moveIndex : function(data, type)
15354 var index = this.indexOf(data);
15356 var newIndex = index + type;
15360 this.insert(newIndex, data);
15365 * Ext JS Library 1.1.1
15366 * Copyright(c) 2006-2007, Ext JS, LLC.
15368 * Originally Released Under LGPL - original licence link has changed is not relivant.
15371 * <script type="text/javascript">
15375 * @class Roo.data.SimpleStore
15376 * @extends Roo.data.Store
15377 * Small helper class to make creating Stores from Array data easier.
15378 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15379 * @cfg {Array} fields An array of field definition objects, or field name strings.
15380 * @cfg {Object} an existing reader (eg. copied from another store)
15381 * @cfg {Array} data The multi-dimensional array of data
15382 * @cfg {Roo.data.DataProxy} proxy [not-required]
15383 * @cfg {Roo.data.Reader} reader [not-required]
15385 * @param {Object} config
15387 Roo.data.SimpleStore = function(config)
15389 Roo.data.SimpleStore.superclass.constructor.call(this, {
15391 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15394 Roo.data.Record.create(config.fields)
15396 proxy : new Roo.data.MemoryProxy(config.data)
15400 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15402 * Ext JS Library 1.1.1
15403 * Copyright(c) 2006-2007, Ext JS, LLC.
15405 * Originally Released Under LGPL - original licence link has changed is not relivant.
15408 * <script type="text/javascript">
15413 * @extends Roo.data.Store
15414 * @class Roo.data.JsonStore
15415 * Small helper class to make creating Stores for JSON data easier. <br/>
15417 var store = new Roo.data.JsonStore({
15418 url: 'get-images.php',
15420 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15423 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15424 * JsonReader and HttpProxy (unless inline data is provided).</b>
15425 * @cfg {Array} fields An array of field definition objects, or field name strings.
15427 * @param {Object} config
15429 Roo.data.JsonStore = function(c){
15430 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15431 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15432 reader: new Roo.data.JsonReader(c, c.fields)
15435 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15437 * Ext JS Library 1.1.1
15438 * Copyright(c) 2006-2007, Ext JS, LLC.
15440 * Originally Released Under LGPL - original licence link has changed is not relivant.
15443 * <script type="text/javascript">
15447 Roo.data.Field = function(config){
15448 if(typeof config == "string"){
15449 config = {name: config};
15451 Roo.apply(this, config);
15454 this.type = "auto";
15457 var st = Roo.data.SortTypes;
15458 // named sortTypes are supported, here we look them up
15459 if(typeof this.sortType == "string"){
15460 this.sortType = st[this.sortType];
15463 // set default sortType for strings and dates
15464 if(!this.sortType){
15467 this.sortType = st.asUCString;
15470 this.sortType = st.asDate;
15473 this.sortType = st.none;
15478 var stripRe = /[\$,%]/g;
15480 // prebuilt conversion function for this field, instead of
15481 // switching every time we're reading a value
15483 var cv, dateFormat = this.dateFormat;
15488 cv = function(v){ return v; };
15491 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15495 return v !== undefined && v !== null && v !== '' ?
15496 parseInt(String(v).replace(stripRe, ""), 10) : '';
15501 return v !== undefined && v !== null && v !== '' ?
15502 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15507 cv = function(v){ return v === true || v === "true" || v == 1; };
15514 if(v instanceof Date){
15518 if(dateFormat == "timestamp"){
15519 return new Date(v*1000);
15521 return Date.parseDate(v, dateFormat);
15523 var parsed = Date.parse(v);
15524 return parsed ? new Date(parsed) : null;
15533 Roo.data.Field.prototype = {
15541 * Ext JS Library 1.1.1
15542 * Copyright(c) 2006-2007, Ext JS, LLC.
15544 * Originally Released Under LGPL - original licence link has changed is not relivant.
15547 * <script type="text/javascript">
15550 // Base class for reading structured data from a data source. This class is intended to be
15551 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15554 * @class Roo.data.DataReader
15556 * Base class for reading structured data from a data source. This class is intended to be
15557 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15560 Roo.data.DataReader = function(meta, recordType){
15564 this.recordType = recordType instanceof Array ?
15565 Roo.data.Record.create(recordType) : recordType;
15568 Roo.data.DataReader.prototype = {
15571 readerType : 'Data',
15573 * Create an empty record
15574 * @param {Object} data (optional) - overlay some values
15575 * @return {Roo.data.Record} record created.
15577 newRow : function(d) {
15579 this.recordType.prototype.fields.each(function(c) {
15581 case 'int' : da[c.name] = 0; break;
15582 case 'date' : da[c.name] = new Date(); break;
15583 case 'float' : da[c.name] = 0.0; break;
15584 case 'boolean' : da[c.name] = false; break;
15585 default : da[c.name] = ""; break;
15589 return new this.recordType(Roo.apply(da, d));
15595 * Ext JS Library 1.1.1
15596 * Copyright(c) 2006-2007, Ext JS, LLC.
15598 * Originally Released Under LGPL - original licence link has changed is not relivant.
15601 * <script type="text/javascript">
15605 * @class Roo.data.DataProxy
15606 * @extends Roo.data.Observable
15608 * This class is an abstract base class for implementations which provide retrieval of
15609 * unformatted data objects.<br>
15611 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15612 * (of the appropriate type which knows how to parse the data object) to provide a block of
15613 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15615 * Custom implementations must implement the load method as described in
15616 * {@link Roo.data.HttpProxy#load}.
15618 Roo.data.DataProxy = function(){
15621 * @event beforeload
15622 * Fires before a network request is made to retrieve a data object.
15623 * @param {Object} This DataProxy object.
15624 * @param {Object} params The params parameter to the load function.
15629 * Fires before the load method's callback is called.
15630 * @param {Object} This DataProxy object.
15631 * @param {Object} o The data object.
15632 * @param {Object} arg The callback argument object passed to the load function.
15636 * @event loadexception
15637 * Fires if an Exception occurs during data retrieval.
15638 * @param {Object} This DataProxy object.
15639 * @param {Object} o The data object.
15640 * @param {Object} arg The callback argument object passed to the load function.
15641 * @param {Object} e The Exception.
15643 loadexception : true
15645 Roo.data.DataProxy.superclass.constructor.call(this);
15648 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15651 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15655 * Ext JS Library 1.1.1
15656 * Copyright(c) 2006-2007, Ext JS, LLC.
15658 * Originally Released Under LGPL - original licence link has changed is not relivant.
15661 * <script type="text/javascript">
15664 * @class Roo.data.MemoryProxy
15665 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15666 * to the Reader when its load method is called.
15668 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15670 Roo.data.MemoryProxy = function(data){
15674 Roo.data.MemoryProxy.superclass.constructor.call(this);
15678 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15681 * Load data from the requested source (in this case an in-memory
15682 * data object passed to the constructor), read the data object into
15683 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15684 * process that block using the passed callback.
15685 * @param {Object} params This parameter is not used by the MemoryProxy class.
15686 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15687 * object into a block of Roo.data.Records.
15688 * @param {Function} callback The function into which to pass the block of Roo.data.records.
15689 * The function must be passed <ul>
15690 * <li>The Record block object</li>
15691 * <li>The "arg" argument from the load function</li>
15692 * <li>A boolean success indicator</li>
15694 * @param {Object} scope The scope in which to call the callback
15695 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15697 load : function(params, reader, callback, scope, arg){
15698 params = params || {};
15701 result = reader.readRecords(params.data ? params.data :this.data);
15703 this.fireEvent("loadexception", this, arg, null, e);
15704 callback.call(scope, null, arg, false);
15707 callback.call(scope, result, arg, true);
15711 update : function(params, records){
15716 * Ext JS Library 1.1.1
15717 * Copyright(c) 2006-2007, Ext JS, LLC.
15719 * Originally Released Under LGPL - original licence link has changed is not relivant.
15722 * <script type="text/javascript">
15725 * @class Roo.data.HttpProxy
15726 * @extends Roo.data.DataProxy
15727 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15728 * configured to reference a certain URL.<br><br>
15730 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15731 * from which the running page was served.<br><br>
15733 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15735 * Be aware that to enable the browser to parse an XML document, the server must set
15736 * the Content-Type header in the HTTP response to "text/xml".
15738 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15739 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
15740 * will be used to make the request.
15742 Roo.data.HttpProxy = function(conn){
15743 Roo.data.HttpProxy.superclass.constructor.call(this);
15744 // is conn a conn config or a real conn?
15746 this.useAjax = !conn || !conn.events;
15750 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15751 // thse are take from connection...
15754 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15757 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15758 * extra parameters to each request made by this object. (defaults to undefined)
15761 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15762 * to each request made by this object. (defaults to undefined)
15765 * @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)
15768 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15771 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15777 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15781 * Return the {@link Roo.data.Connection} object being used by this Proxy.
15782 * @return {Connection} The Connection object. This object may be used to subscribe to events on
15783 * a finer-grained basis than the DataProxy events.
15785 getConnection : function(){
15786 return this.useAjax ? Roo.Ajax : this.conn;
15790 * Load data from the configured {@link Roo.data.Connection}, read the data object into
15791 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15792 * process that block using the passed callback.
15793 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15794 * for the request to the remote server.
15795 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15796 * object into a block of Roo.data.Records.
15797 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15798 * The function must be passed <ul>
15799 * <li>The Record block object</li>
15800 * <li>The "arg" argument from the load function</li>
15801 * <li>A boolean success indicator</li>
15803 * @param {Object} scope The scope in which to call the callback
15804 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15806 load : function(params, reader, callback, scope, arg){
15807 if(this.fireEvent("beforeload", this, params) !== false){
15809 params : params || {},
15811 callback : callback,
15816 callback : this.loadResponse,
15820 Roo.applyIf(o, this.conn);
15821 if(this.activeRequest){
15822 Roo.Ajax.abort(this.activeRequest);
15824 this.activeRequest = Roo.Ajax.request(o);
15826 this.conn.request(o);
15829 callback.call(scope||this, null, arg, false);
15834 loadResponse : function(o, success, response){
15835 delete this.activeRequest;
15837 this.fireEvent("loadexception", this, o, response);
15838 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15843 result = o.reader.read(response);
15845 this.fireEvent("loadexception", this, o, response, e);
15846 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15850 this.fireEvent("load", this, o, o.request.arg);
15851 o.request.callback.call(o.request.scope, result, o.request.arg, true);
15855 update : function(dataSet){
15860 updateResponse : function(dataSet){
15865 * Ext JS Library 1.1.1
15866 * Copyright(c) 2006-2007, Ext JS, LLC.
15868 * Originally Released Under LGPL - original licence link has changed is not relivant.
15871 * <script type="text/javascript">
15875 * @class Roo.data.ScriptTagProxy
15876 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15877 * other than the originating domain of the running page.<br><br>
15879 * <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
15880 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15882 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15883 * source code that is used as the source inside a <script> tag.<br><br>
15885 * In order for the browser to process the returned data, the server must wrap the data object
15886 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15887 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15888 * depending on whether the callback name was passed:
15891 boolean scriptTag = false;
15892 String cb = request.getParameter("callback");
15895 response.setContentType("text/javascript");
15897 response.setContentType("application/x-json");
15899 Writer out = response.getWriter();
15901 out.write(cb + "(");
15903 out.print(dataBlock.toJsonString());
15910 * @param {Object} config A configuration object.
15912 Roo.data.ScriptTagProxy = function(config){
15913 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15914 Roo.apply(this, config);
15915 this.head = document.getElementsByTagName("head")[0];
15918 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15920 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15922 * @cfg {String} url The URL from which to request the data object.
15925 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15929 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15930 * the server the name of the callback function set up by the load call to process the returned data object.
15931 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15932 * javascript output which calls this named function passing the data object as its only parameter.
15934 callbackParam : "callback",
15936 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15937 * name to the request.
15942 * Load data from the configured URL, read the data object into
15943 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15944 * process that block using the passed callback.
15945 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15946 * for the request to the remote server.
15947 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15948 * object into a block of Roo.data.Records.
15949 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15950 * The function must be passed <ul>
15951 * <li>The Record block object</li>
15952 * <li>The "arg" argument from the load function</li>
15953 * <li>A boolean success indicator</li>
15955 * @param {Object} scope The scope in which to call the callback
15956 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15958 load : function(params, reader, callback, scope, arg){
15959 if(this.fireEvent("beforeload", this, params) !== false){
15961 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15963 var url = this.url;
15964 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15966 url += "&_dc=" + (new Date().getTime());
15968 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15971 cb : "stcCallback"+transId,
15972 scriptId : "stcScript"+transId,
15976 callback : callback,
15982 window[trans.cb] = function(o){
15983 conn.handleResponse(o, trans);
15986 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15988 if(this.autoAbort !== false){
15992 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15994 var script = document.createElement("script");
15995 script.setAttribute("src", url);
15996 script.setAttribute("type", "text/javascript");
15997 script.setAttribute("id", trans.scriptId);
15998 this.head.appendChild(script);
16000 this.trans = trans;
16002 callback.call(scope||this, null, arg, false);
16007 isLoading : function(){
16008 return this.trans ? true : false;
16012 * Abort the current server request.
16014 abort : function(){
16015 if(this.isLoading()){
16016 this.destroyTrans(this.trans);
16021 destroyTrans : function(trans, isLoaded){
16022 this.head.removeChild(document.getElementById(trans.scriptId));
16023 clearTimeout(trans.timeoutId);
16025 window[trans.cb] = undefined;
16027 delete window[trans.cb];
16030 // if hasn't been loaded, wait for load to remove it to prevent script error
16031 window[trans.cb] = function(){
16032 window[trans.cb] = undefined;
16034 delete window[trans.cb];
16041 handleResponse : function(o, trans){
16042 this.trans = false;
16043 this.destroyTrans(trans, true);
16046 result = trans.reader.readRecords(o);
16048 this.fireEvent("loadexception", this, o, trans.arg, e);
16049 trans.callback.call(trans.scope||window, null, trans.arg, false);
16052 this.fireEvent("load", this, o, trans.arg);
16053 trans.callback.call(trans.scope||window, result, trans.arg, true);
16057 handleFailure : function(trans){
16058 this.trans = false;
16059 this.destroyTrans(trans, false);
16060 this.fireEvent("loadexception", this, null, trans.arg);
16061 trans.callback.call(trans.scope||window, null, trans.arg, false);
16065 * Ext JS Library 1.1.1
16066 * Copyright(c) 2006-2007, Ext JS, LLC.
16068 * Originally Released Under LGPL - original licence link has changed is not relivant.
16071 * <script type="text/javascript">
16075 * @class Roo.data.JsonReader
16076 * @extends Roo.data.DataReader
16077 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16078 * based on mappings in a provided Roo.data.Record constructor.
16080 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16081 * in the reply previously.
16086 var RecordDef = Roo.data.Record.create([
16087 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16088 {name: 'occupation'} // This field will use "occupation" as the mapping.
16090 var myReader = new Roo.data.JsonReader({
16091 totalProperty: "results", // The property which contains the total dataset size (optional)
16092 root: "rows", // The property which contains an Array of row objects
16093 id: "id" // The property within each row object that provides an ID for the record (optional)
16097 * This would consume a JSON file like this:
16099 { 'results': 2, 'rows': [
16100 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16101 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16104 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16105 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16106 * paged from the remote server.
16107 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16108 * @cfg {String} root name of the property which contains the Array of row objects.
16109 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16110 * @cfg {Array} fields Array of field definition objects
16112 * Create a new JsonReader
16113 * @param {Object} meta Metadata configuration options
16114 * @param {Object} recordType Either an Array of field definition objects,
16115 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16117 Roo.data.JsonReader = function(meta, recordType){
16120 // set some defaults:
16121 Roo.applyIf(meta, {
16122 totalProperty: 'total',
16123 successProperty : 'success',
16128 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16130 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16132 readerType : 'Json',
16135 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16136 * Used by Store query builder to append _requestMeta to params.
16139 metaFromRemote : false,
16141 * This method is only used by a DataProxy which has retrieved data from a remote server.
16142 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16143 * @return {Object} data A data block which is used by an Roo.data.Store object as
16144 * a cache of Roo.data.Records.
16146 read : function(response){
16147 var json = response.responseText;
16149 var o = /* eval:var:o */ eval("("+json+")");
16151 throw {message: "JsonReader.read: Json object not found"};
16157 this.metaFromRemote = true;
16158 this.meta = o.metaData;
16159 this.recordType = Roo.data.Record.create(o.metaData.fields);
16160 this.onMetaChange(this.meta, this.recordType, o);
16162 return this.readRecords(o);
16165 // private function a store will implement
16166 onMetaChange : function(meta, recordType, o){
16173 simpleAccess: function(obj, subsc) {
16180 getJsonAccessor: function(){
16182 return function(expr) {
16184 return(re.test(expr))
16185 ? new Function("obj", "return obj." + expr)
16190 return Roo.emptyFn;
16195 * Create a data block containing Roo.data.Records from an XML document.
16196 * @param {Object} o An object which contains an Array of row objects in the property specified
16197 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16198 * which contains the total size of the dataset.
16199 * @return {Object} data A data block which is used by an Roo.data.Store object as
16200 * a cache of Roo.data.Records.
16202 readRecords : function(o){
16204 * After any data loads, the raw JSON data is available for further custom processing.
16208 var s = this.meta, Record = this.recordType,
16209 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16211 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16213 if(s.totalProperty) {
16214 this.getTotal = this.getJsonAccessor(s.totalProperty);
16216 if(s.successProperty) {
16217 this.getSuccess = this.getJsonAccessor(s.successProperty);
16219 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16221 var g = this.getJsonAccessor(s.id);
16222 this.getId = function(rec) {
16224 return (r === undefined || r === "") ? null : r;
16227 this.getId = function(){return null;};
16230 for(var jj = 0; jj < fl; jj++){
16232 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16233 this.ef[jj] = this.getJsonAccessor(map);
16237 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16238 if(s.totalProperty){
16239 var vt = parseInt(this.getTotal(o), 10);
16244 if(s.successProperty){
16245 var vs = this.getSuccess(o);
16246 if(vs === false || vs === 'false'){
16251 for(var i = 0; i < c; i++){
16254 var id = this.getId(n);
16255 for(var j = 0; j < fl; j++){
16257 var v = this.ef[j](n);
16259 Roo.log('missing convert for ' + f.name);
16263 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16265 var record = new Record(values, id);
16267 records[i] = record;
16273 totalRecords : totalRecords
16276 // used when loading children.. @see loadDataFromChildren
16277 toLoadData: function(rec)
16279 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16280 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16281 return { data : data, total : data.length };
16286 * Ext JS Library 1.1.1
16287 * Copyright(c) 2006-2007, Ext JS, LLC.
16289 * Originally Released Under LGPL - original licence link has changed is not relivant.
16292 * <script type="text/javascript">
16296 * @class Roo.data.ArrayReader
16297 * @extends Roo.data.DataReader
16298 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16299 * Each element of that Array represents a row of data fields. The
16300 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16301 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16305 var RecordDef = Roo.data.Record.create([
16306 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16307 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16309 var myReader = new Roo.data.ArrayReader({
16310 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16314 * This would consume an Array like this:
16316 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16320 * Create a new JsonReader
16321 * @param {Object} meta Metadata configuration options.
16322 * @param {Object|Array} recordType Either an Array of field definition objects
16324 * @cfg {Array} fields Array of field definition objects
16325 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16326 * as specified to {@link Roo.data.Record#create},
16327 * or an {@link Roo.data.Record} object
16330 * created using {@link Roo.data.Record#create}.
16332 Roo.data.ArrayReader = function(meta, recordType)
16334 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16337 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16340 * Create a data block containing Roo.data.Records from an XML document.
16341 * @param {Object} o An Array of row objects which represents the dataset.
16342 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16343 * a cache of Roo.data.Records.
16345 readRecords : function(o)
16347 var sid = this.meta ? this.meta.id : null;
16348 var recordType = this.recordType, fields = recordType.prototype.fields;
16351 for(var i = 0; i < root.length; i++){
16354 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16355 for(var j = 0, jlen = fields.length; j < jlen; j++){
16356 var f = fields.items[j];
16357 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16358 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16360 values[f.name] = v;
16362 var record = new recordType(values, id);
16364 records[records.length] = record;
16368 totalRecords : records.length
16371 // used when loading children.. @see loadDataFromChildren
16372 toLoadData: function(rec)
16374 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16375 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16386 * @class Roo.bootstrap.ComboBox
16387 * @extends Roo.bootstrap.TriggerField
16388 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16389 * @cfg {Boolean} append (true|false) default false
16390 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16391 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16392 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16393 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16394 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16395 * @cfg {Boolean} animate default true
16396 * @cfg {Boolean} emptyResultText only for touch device
16397 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16398 * @cfg {String} emptyTitle default ''
16399 * @cfg {Number} width fixed with? experimental
16401 * Create a new ComboBox.
16402 * @param {Object} config Configuration options
16404 Roo.bootstrap.ComboBox = function(config){
16405 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16409 * Fires when the dropdown list is expanded
16410 * @param {Roo.bootstrap.ComboBox} combo This combo box
16415 * Fires when the dropdown list is collapsed
16416 * @param {Roo.bootstrap.ComboBox} combo This combo box
16420 * @event beforeselect
16421 * Fires before a list item is selected. Return false to cancel the selection.
16422 * @param {Roo.bootstrap.ComboBox} combo This combo box
16423 * @param {Roo.data.Record} record The data record returned from the underlying store
16424 * @param {Number} index The index of the selected item in the dropdown list
16426 'beforeselect' : true,
16429 * Fires when a list item is selected
16430 * @param {Roo.bootstrap.ComboBox} combo This combo box
16431 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16432 * @param {Number} index The index of the selected item in the dropdown list
16436 * @event beforequery
16437 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16438 * The event object passed has these properties:
16439 * @param {Roo.bootstrap.ComboBox} combo This combo box
16440 * @param {String} query The query
16441 * @param {Boolean} forceAll true to force "all" query
16442 * @param {Boolean} cancel true to cancel the query
16443 * @param {Object} e The query event object
16445 'beforequery': true,
16448 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16449 * @param {Roo.bootstrap.ComboBox} combo This combo box
16454 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16455 * @param {Roo.bootstrap.ComboBox} combo This combo box
16456 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16461 * Fires when the remove value from the combobox array
16462 * @param {Roo.bootstrap.ComboBox} combo This combo box
16466 * @event afterremove
16467 * Fires when the remove value from the combobox array
16468 * @param {Roo.bootstrap.ComboBox} combo This combo box
16470 'afterremove' : true,
16472 * @event specialfilter
16473 * Fires when specialfilter
16474 * @param {Roo.bootstrap.ComboBox} combo This combo box
16476 'specialfilter' : true,
16479 * Fires when tick the element
16480 * @param {Roo.bootstrap.ComboBox} combo This combo box
16484 * @event touchviewdisplay
16485 * Fires when touch view require special display (default is using displayField)
16486 * @param {Roo.bootstrap.ComboBox} combo This combo box
16487 * @param {Object} cfg set html .
16489 'touchviewdisplay' : true
16494 this.tickItems = [];
16496 this.selectedIndex = -1;
16497 if(this.mode == 'local'){
16498 if(config.queryDelay === undefined){
16499 this.queryDelay = 10;
16501 if(config.minChars === undefined){
16507 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16510 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16511 * rendering into an Roo.Editor, defaults to false)
16514 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16515 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16518 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16521 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16522 * the dropdown list (defaults to undefined, with no header element)
16526 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16530 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16532 listWidth: undefined,
16534 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16535 * mode = 'remote' or 'text' if mode = 'local')
16537 displayField: undefined,
16540 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16541 * mode = 'remote' or 'value' if mode = 'local').
16542 * Note: use of a valueField requires the user make a selection
16543 * in order for a value to be mapped.
16545 valueField: undefined,
16547 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16552 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16553 * field's data value (defaults to the underlying DOM element's name)
16555 hiddenName: undefined,
16557 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16561 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16563 selectedClass: 'active',
16566 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16570 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16571 * anchor positions (defaults to 'tl-bl')
16573 listAlign: 'tl-bl?',
16575 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16579 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16580 * query specified by the allQuery config option (defaults to 'query')
16582 triggerAction: 'query',
16584 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16585 * (defaults to 4, does not apply if editable = false)
16589 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16590 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16594 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16595 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16599 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16600 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16604 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16605 * when editable = true (defaults to false)
16607 selectOnFocus:false,
16609 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16611 queryParam: 'query',
16613 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
16614 * when mode = 'remote' (defaults to 'Loading...')
16616 loadingText: 'Loading...',
16618 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16622 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16626 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16627 * traditional select (defaults to true)
16631 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16635 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16639 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16640 * listWidth has a higher value)
16644 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16645 * allow the user to set arbitrary text into the field (defaults to false)
16647 forceSelection:false,
16649 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16650 * if typeAhead = true (defaults to 250)
16652 typeAheadDelay : 250,
16654 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16655 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16657 valueNotFoundText : undefined,
16659 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16661 blockFocus : false,
16664 * @cfg {Boolean} disableClear Disable showing of clear button.
16666 disableClear : false,
16668 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
16670 alwaysQuery : false,
16673 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
16678 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16680 invalidClass : "has-warning",
16683 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16685 validClass : "has-success",
16688 * @cfg {Boolean} specialFilter (true|false) special filter default false
16690 specialFilter : false,
16693 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16695 mobileTouchView : true,
16698 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16700 useNativeIOS : false,
16703 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16705 mobile_restrict_height : false,
16707 ios_options : false,
16719 btnPosition : 'right',
16720 triggerList : true,
16721 showToggleBtn : true,
16723 emptyResultText: 'Empty',
16724 triggerText : 'Select',
16728 // element that contains real text value.. (when hidden is used..)
16730 getAutoCreate : function()
16735 * Render classic select for iso
16738 if(Roo.isIOS && this.useNativeIOS){
16739 cfg = this.getAutoCreateNativeIOS();
16747 if(Roo.isTouch && this.mobileTouchView){
16748 cfg = this.getAutoCreateTouchView();
16755 if(!this.tickable){
16756 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16761 * ComboBox with tickable selections
16764 var align = this.labelAlign || this.parentLabelAlign();
16767 cls : 'form-group roo-combobox-tickable' //input-group
16770 var btn_text_select = '';
16771 var btn_text_done = '';
16772 var btn_text_cancel = '';
16774 if (this.btn_text_show) {
16775 btn_text_select = 'Select';
16776 btn_text_done = 'Done';
16777 btn_text_cancel = 'Cancel';
16782 cls : 'tickable-buttons',
16787 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16788 //html : this.triggerText
16789 html: btn_text_select
16795 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16797 html: btn_text_done
16803 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16805 html: btn_text_cancel
16811 buttons.cn.unshift({
16813 cls: 'roo-select2-search-field-input'
16819 Roo.each(buttons.cn, function(c){
16821 c.cls += ' btn-' + _this.size;
16824 if (_this.disabled) {
16831 style : 'display: contents',
16836 cls: 'form-hidden-field'
16840 cls: 'roo-select2-choices',
16844 cls: 'roo-select2-search-field',
16855 cls: 'roo-select2-container input-group roo-select2-container-multi',
16861 // cls: 'typeahead typeahead-long dropdown-menu',
16862 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
16867 if(this.hasFeedback && !this.allowBlank){
16871 cls: 'glyphicon form-control-feedback'
16874 combobox.cn.push(feedback);
16881 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16882 tooltip : 'This field is required'
16884 if (Roo.bootstrap.version == 4) {
16887 style : 'display:none'
16890 if (align ==='left' && this.fieldLabel.length) {
16892 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
16899 cls : 'control-label col-form-label',
16900 html : this.fieldLabel
16912 var labelCfg = cfg.cn[1];
16913 var contentCfg = cfg.cn[2];
16916 if(this.indicatorpos == 'right'){
16922 cls : 'control-label col-form-label',
16926 html : this.fieldLabel
16942 labelCfg = cfg.cn[0];
16943 contentCfg = cfg.cn[1];
16947 if(this.labelWidth > 12){
16948 labelCfg.style = "width: " + this.labelWidth + 'px';
16950 if(this.width * 1 > 0){
16951 contentCfg.style = "width: " + this.width + 'px';
16953 if(this.labelWidth < 13 && this.labelmd == 0){
16954 this.labelmd = this.labelWidth;
16957 if(this.labellg > 0){
16958 labelCfg.cls += ' col-lg-' + this.labellg;
16959 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16962 if(this.labelmd > 0){
16963 labelCfg.cls += ' col-md-' + this.labelmd;
16964 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16967 if(this.labelsm > 0){
16968 labelCfg.cls += ' col-sm-' + this.labelsm;
16969 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16972 if(this.labelxs > 0){
16973 labelCfg.cls += ' col-xs-' + this.labelxs;
16974 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16978 } else if ( this.fieldLabel.length) {
16979 // Roo.log(" label");
16984 //cls : 'input-group-addon',
16985 html : this.fieldLabel
16990 if(this.indicatorpos == 'right'){
16994 //cls : 'input-group-addon',
16995 html : this.fieldLabel
17005 // Roo.log(" no label && no align");
17012 ['xs','sm','md','lg'].map(function(size){
17013 if (settings[size]) {
17014 cfg.cls += ' col-' + size + '-' + settings[size];
17022 _initEventsCalled : false,
17025 initEvents: function()
17027 if (this._initEventsCalled) { // as we call render... prevent looping...
17030 this._initEventsCalled = true;
17033 throw "can not find store for combo";
17036 this.indicator = this.indicatorEl();
17038 this.store = Roo.factory(this.store, Roo.data);
17039 this.store.parent = this;
17041 // if we are building from html. then this element is so complex, that we can not really
17042 // use the rendered HTML.
17043 // so we have to trash and replace the previous code.
17044 if (Roo.XComponent.build_from_html) {
17045 // remove this element....
17046 var e = this.el.dom, k=0;
17047 while (e ) { e = e.previousSibling; ++k;}
17052 this.rendered = false;
17054 this.render(this.parent().getChildContainer(true), k);
17057 if(Roo.isIOS && this.useNativeIOS){
17058 this.initIOSView();
17066 if(Roo.isTouch && this.mobileTouchView){
17067 this.initTouchView();
17072 this.initTickableEvents();
17076 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17078 if(this.hiddenName){
17080 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17082 this.hiddenField.dom.value =
17083 this.hiddenValue !== undefined ? this.hiddenValue :
17084 this.value !== undefined ? this.value : '';
17086 // prevent input submission
17087 this.el.dom.removeAttribute('name');
17088 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17093 // this.el.dom.setAttribute('autocomplete', 'off');
17096 var cls = 'x-combo-list';
17098 //this.list = new Roo.Layer({
17099 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17105 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17106 _this.list.setWidth(lw);
17109 this.list.on('mouseover', this.onViewOver, this);
17110 this.list.on('mousemove', this.onViewMove, this);
17111 this.list.on('scroll', this.onViewScroll, this);
17114 this.list.swallowEvent('mousewheel');
17115 this.assetHeight = 0;
17118 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17119 this.assetHeight += this.header.getHeight();
17122 this.innerList = this.list.createChild({cls:cls+'-inner'});
17123 this.innerList.on('mouseover', this.onViewOver, this);
17124 this.innerList.on('mousemove', this.onViewMove, this);
17125 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17127 if(this.allowBlank && !this.pageSize && !this.disableClear){
17128 this.footer = this.list.createChild({cls:cls+'-ft'});
17129 this.pageTb = new Roo.Toolbar(this.footer);
17133 this.footer = this.list.createChild({cls:cls+'-ft'});
17134 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17135 {pageSize: this.pageSize});
17139 if (this.pageTb && this.allowBlank && !this.disableClear) {
17141 this.pageTb.add(new Roo.Toolbar.Fill(), {
17142 cls: 'x-btn-icon x-btn-clear',
17144 handler: function()
17147 _this.clearValue();
17148 _this.onSelect(false, -1);
17153 this.assetHeight += this.footer.getHeight();
17158 this.tpl = Roo.bootstrap.version == 4 ?
17159 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17160 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17163 this.view = new Roo.View(this.list, this.tpl, {
17164 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17166 //this.view.wrapEl.setDisplayed(false);
17167 this.view.on('click', this.onViewClick, this);
17170 this.store.on('beforeload', this.onBeforeLoad, this);
17171 this.store.on('load', this.onLoad, this);
17172 this.store.on('loadexception', this.onLoadException, this);
17174 if(this.resizable){
17175 this.resizer = new Roo.Resizable(this.list, {
17176 pinned:true, handles:'se'
17178 this.resizer.on('resize', function(r, w, h){
17179 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17180 this.listWidth = w;
17181 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17182 this.restrictHeight();
17184 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17187 if(!this.editable){
17188 this.editable = true;
17189 this.setEditable(false);
17194 if (typeof(this.events.add.listeners) != 'undefined') {
17196 this.addicon = this.wrap.createChild(
17197 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17199 this.addicon.on('click', function(e) {
17200 this.fireEvent('add', this);
17203 if (typeof(this.events.edit.listeners) != 'undefined') {
17205 this.editicon = this.wrap.createChild(
17206 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17207 if (this.addicon) {
17208 this.editicon.setStyle('margin-left', '40px');
17210 this.editicon.on('click', function(e) {
17212 // we fire even if inothing is selected..
17213 this.fireEvent('edit', this, this.lastData );
17219 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17220 "up" : function(e){
17221 this.inKeyMode = true;
17225 "down" : function(e){
17226 if(!this.isExpanded()){
17227 this.onTriggerClick();
17229 this.inKeyMode = true;
17234 "enter" : function(e){
17235 // this.onViewClick();
17239 if(this.fireEvent("specialkey", this, e)){
17240 this.onViewClick(false);
17246 "esc" : function(e){
17250 "tab" : function(e){
17253 if(this.fireEvent("specialkey", this, e)){
17254 this.onViewClick(false);
17262 doRelay : function(foo, bar, hname){
17263 if(hname == 'down' || this.scope.isExpanded()){
17264 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17273 this.queryDelay = Math.max(this.queryDelay || 10,
17274 this.mode == 'local' ? 10 : 250);
17277 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17279 if(this.typeAhead){
17280 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17282 if(this.editable !== false){
17283 this.inputEl().on("keyup", this.onKeyUp, this);
17285 if(this.forceSelection){
17286 this.inputEl().on('blur', this.doForce, this);
17290 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17291 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17295 initTickableEvents: function()
17299 if(this.hiddenName){
17301 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17303 this.hiddenField.dom.value =
17304 this.hiddenValue !== undefined ? this.hiddenValue :
17305 this.value !== undefined ? this.value : '';
17307 // prevent input submission
17308 this.el.dom.removeAttribute('name');
17309 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17314 // this.list = this.el.select('ul.dropdown-menu',true).first();
17316 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17317 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17318 if(this.triggerList){
17319 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17322 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17323 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17325 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17326 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17328 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17329 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17331 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17332 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17333 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17336 this.cancelBtn.hide();
17341 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17342 _this.list.setWidth(lw);
17345 this.list.on('mouseover', this.onViewOver, this);
17346 this.list.on('mousemove', this.onViewMove, this);
17348 this.list.on('scroll', this.onViewScroll, this);
17351 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17352 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17355 this.view = new Roo.View(this.list, this.tpl, {
17360 selectedClass: this.selectedClass
17363 //this.view.wrapEl.setDisplayed(false);
17364 this.view.on('click', this.onViewClick, this);
17368 this.store.on('beforeload', this.onBeforeLoad, this);
17369 this.store.on('load', this.onLoad, this);
17370 this.store.on('loadexception', this.onLoadException, this);
17373 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17374 "up" : function(e){
17375 this.inKeyMode = true;
17379 "down" : function(e){
17380 this.inKeyMode = true;
17384 "enter" : function(e){
17385 if(this.fireEvent("specialkey", this, e)){
17386 this.onViewClick(false);
17392 "esc" : function(e){
17393 this.onTickableFooterButtonClick(e, false, false);
17396 "tab" : function(e){
17397 this.fireEvent("specialkey", this, e);
17399 this.onTickableFooterButtonClick(e, false, false);
17406 doRelay : function(e, fn, key){
17407 if(this.scope.isExpanded()){
17408 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17417 this.queryDelay = Math.max(this.queryDelay || 10,
17418 this.mode == 'local' ? 10 : 250);
17421 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17423 if(this.typeAhead){
17424 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17427 if(this.editable !== false){
17428 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17431 this.indicator = this.indicatorEl();
17433 if(this.indicator){
17434 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17435 this.indicator.hide();
17440 onDestroy : function(){
17442 this.view.setStore(null);
17443 this.view.el.removeAllListeners();
17444 this.view.el.remove();
17445 this.view.purgeListeners();
17448 this.list.dom.innerHTML = '';
17452 this.store.un('beforeload', this.onBeforeLoad, this);
17453 this.store.un('load', this.onLoad, this);
17454 this.store.un('loadexception', this.onLoadException, this);
17456 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17460 fireKey : function(e){
17461 if(e.isNavKeyPress() && !this.list.isVisible()){
17462 this.fireEvent("specialkey", this, e);
17467 onResize: function(w, h)
17471 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17473 // if(typeof w != 'number'){
17474 // // we do not handle it!?!?
17477 // var tw = this.trigger.getWidth();
17478 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17479 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17481 // this.inputEl().setWidth( this.adjustWidth('input', x));
17483 // //this.trigger.setStyle('left', x+'px');
17485 // if(this.list && this.listWidth === undefined){
17486 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17487 // this.list.setWidth(lw);
17488 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17496 * Allow or prevent the user from directly editing the field text. If false is passed,
17497 * the user will only be able to select from the items defined in the dropdown list. This method
17498 * is the runtime equivalent of setting the 'editable' config option at config time.
17499 * @param {Boolean} value True to allow the user to directly edit the field text
17501 setEditable : function(value){
17502 if(value == this.editable){
17505 this.editable = value;
17507 this.inputEl().dom.setAttribute('readOnly', true);
17508 this.inputEl().on('mousedown', this.onTriggerClick, this);
17509 this.inputEl().addClass('x-combo-noedit');
17511 this.inputEl().dom.removeAttribute('readOnly');
17512 this.inputEl().un('mousedown', this.onTriggerClick, this);
17513 this.inputEl().removeClass('x-combo-noedit');
17519 onBeforeLoad : function(combo,opts){
17520 if(!this.hasFocus){
17524 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17526 this.restrictHeight();
17527 this.selectedIndex = -1;
17531 onLoad : function(){
17533 this.hasQuery = false;
17535 if(!this.hasFocus){
17539 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17540 this.loading.hide();
17543 if(this.store.getCount() > 0){
17546 this.restrictHeight();
17547 if(this.lastQuery == this.allQuery){
17548 if(this.editable && !this.tickable){
17549 this.inputEl().dom.select();
17553 !this.selectByValue(this.value, true) &&
17556 !this.store.lastOptions ||
17557 typeof(this.store.lastOptions.add) == 'undefined' ||
17558 this.store.lastOptions.add != true
17561 this.select(0, true);
17564 if(this.autoFocus){
17567 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17568 this.taTask.delay(this.typeAheadDelay);
17572 this.onEmptyResults();
17578 onLoadException : function()
17580 this.hasQuery = false;
17582 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17583 this.loading.hide();
17586 if(this.tickable && this.editable){
17591 // only causes errors at present
17592 //Roo.log(this.store.reader.jsonData);
17593 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17595 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17601 onTypeAhead : function(){
17602 if(this.store.getCount() > 0){
17603 var r = this.store.getAt(0);
17604 var newValue = r.data[this.displayField];
17605 var len = newValue.length;
17606 var selStart = this.getRawValue().length;
17608 if(selStart != len){
17609 this.setRawValue(newValue);
17610 this.selectText(selStart, newValue.length);
17616 onSelect : function(record, index){
17618 if(this.fireEvent('beforeselect', this, record, index) !== false){
17620 this.setFromData(index > -1 ? record.data : false);
17623 this.fireEvent('select', this, record, index);
17628 * Returns the currently selected field value or empty string if no value is set.
17629 * @return {String} value The selected value
17631 getValue : function()
17633 if(Roo.isIOS && this.useNativeIOS){
17634 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17638 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17641 if(this.valueField){
17642 return typeof this.value != 'undefined' ? this.value : '';
17644 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17648 getRawValue : function()
17650 if(Roo.isIOS && this.useNativeIOS){
17651 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17654 var v = this.inputEl().getValue();
17660 * Clears any text/value currently set in the field
17662 clearValue : function(){
17664 if(this.hiddenField){
17665 this.hiddenField.dom.value = '';
17668 this.setRawValue('');
17669 this.lastSelectionText = '';
17670 this.lastData = false;
17672 var close = this.closeTriggerEl();
17683 * Sets the specified value into the field. If the value finds a match, the corresponding record text
17684 * will be displayed in the field. If the value does not match the data value of an existing item,
17685 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17686 * Otherwise the field will be blank (although the value will still be set).
17687 * @param {String} value The value to match
17689 setValue : function(v)
17691 if(Roo.isIOS && this.useNativeIOS){
17692 this.setIOSValue(v);
17702 if(this.valueField){
17703 var r = this.findRecord(this.valueField, v);
17705 text = r.data[this.displayField];
17706 }else if(this.valueNotFoundText !== undefined){
17707 text = this.valueNotFoundText;
17710 this.lastSelectionText = text;
17711 if(this.hiddenField){
17712 this.hiddenField.dom.value = v;
17714 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17717 var close = this.closeTriggerEl();
17720 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17726 * @property {Object} the last set data for the element
17731 * Sets the value of the field based on a object which is related to the record format for the store.
17732 * @param {Object} value the value to set as. or false on reset?
17734 setFromData : function(o){
17741 var dv = ''; // display value
17742 var vv = ''; // value value..
17744 if (this.displayField) {
17745 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17747 // this is an error condition!!!
17748 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17751 if(this.valueField){
17752 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17755 var close = this.closeTriggerEl();
17758 if(dv.length || vv * 1 > 0){
17760 this.blockFocus=true;
17766 if(this.hiddenField){
17767 this.hiddenField.dom.value = vv;
17769 this.lastSelectionText = dv;
17770 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17774 // no hidden field.. - we store the value in 'value', but still display
17775 // display field!!!!
17776 this.lastSelectionText = dv;
17777 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17784 reset : function(){
17785 // overridden so that last data is reset..
17792 this.setValue(this.originalValue);
17793 //this.clearInvalid();
17794 this.lastData = false;
17796 this.view.clearSelections();
17802 findRecord : function(prop, value){
17804 if(this.store.getCount() > 0){
17805 this.store.each(function(r){
17806 if(r.data[prop] == value){
17816 getName: function()
17818 // returns hidden if it's set..
17819 if (!this.rendered) {return ''};
17820 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
17824 onViewMove : function(e, t){
17825 this.inKeyMode = false;
17829 onViewOver : function(e, t){
17830 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17833 var item = this.view.findItemFromChild(t);
17836 var index = this.view.indexOf(item);
17837 this.select(index, false);
17842 onViewClick : function(view, doFocus, el, e)
17844 var index = this.view.getSelectedIndexes()[0];
17846 var r = this.store.getAt(index);
17850 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17857 Roo.each(this.tickItems, function(v,k){
17859 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17861 _this.tickItems.splice(k, 1);
17863 if(typeof(e) == 'undefined' && view == false){
17864 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17876 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17877 this.tickItems.push(r.data);
17880 if(typeof(e) == 'undefined' && view == false){
17881 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17888 this.onSelect(r, index);
17890 if(doFocus !== false && !this.blockFocus){
17891 this.inputEl().focus();
17896 restrictHeight : function(){
17897 //this.innerList.dom.style.height = '';
17898 //var inner = this.innerList.dom;
17899 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17900 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17901 //this.list.beginUpdate();
17902 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17903 this.list.alignTo(this.inputEl(), this.listAlign);
17904 this.list.alignTo(this.inputEl(), this.listAlign);
17905 //this.list.endUpdate();
17909 onEmptyResults : function(){
17911 if(this.tickable && this.editable){
17912 this.hasFocus = false;
17913 this.restrictHeight();
17921 * Returns true if the dropdown list is expanded, else false.
17923 isExpanded : function(){
17924 return this.list.isVisible();
17928 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17929 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17930 * @param {String} value The data value of the item to select
17931 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17932 * selected item if it is not currently in view (defaults to true)
17933 * @return {Boolean} True if the value matched an item in the list, else false
17935 selectByValue : function(v, scrollIntoView){
17936 if(v !== undefined && v !== null){
17937 var r = this.findRecord(this.valueField || this.displayField, v);
17939 this.select(this.store.indexOf(r), scrollIntoView);
17947 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17948 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17949 * @param {Number} index The zero-based index of the list item to select
17950 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17951 * selected item if it is not currently in view (defaults to true)
17953 select : function(index, scrollIntoView){
17954 this.selectedIndex = index;
17955 this.view.select(index);
17956 if(scrollIntoView !== false){
17957 var el = this.view.getNode(index);
17959 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17962 this.list.scrollChildIntoView(el, false);
17968 selectNext : function(){
17969 var ct = this.store.getCount();
17971 if(this.selectedIndex == -1){
17973 }else if(this.selectedIndex < ct-1){
17974 this.select(this.selectedIndex+1);
17980 selectPrev : function(){
17981 var ct = this.store.getCount();
17983 if(this.selectedIndex == -1){
17985 }else if(this.selectedIndex != 0){
17986 this.select(this.selectedIndex-1);
17992 onKeyUp : function(e){
17993 if(this.editable !== false && !e.isSpecialKey()){
17994 this.lastKey = e.getKey();
17995 this.dqTask.delay(this.queryDelay);
18000 validateBlur : function(){
18001 return !this.list || !this.list.isVisible();
18005 initQuery : function(){
18007 var v = this.getRawValue();
18009 if(this.tickable && this.editable){
18010 v = this.tickableInputEl().getValue();
18017 doForce : function(){
18018 if(this.inputEl().dom.value.length > 0){
18019 this.inputEl().dom.value =
18020 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18026 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
18027 * query allowing the query action to be canceled if needed.
18028 * @param {String} query The SQL query to execute
18029 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18030 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18031 * saved in the current store (defaults to false)
18033 doQuery : function(q, forceAll){
18035 if(q === undefined || q === null){
18040 forceAll: forceAll,
18044 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18049 forceAll = qe.forceAll;
18050 if(forceAll === true || (q.length >= this.minChars)){
18052 this.hasQuery = true;
18054 if(this.lastQuery != q || this.alwaysQuery){
18055 this.lastQuery = q;
18056 if(this.mode == 'local'){
18057 this.selectedIndex = -1;
18059 this.store.clearFilter();
18062 if(this.specialFilter){
18063 this.fireEvent('specialfilter', this);
18068 this.store.filter(this.displayField, q);
18071 this.store.fireEvent("datachanged", this.store);
18078 this.store.baseParams[this.queryParam] = q;
18080 var options = {params : this.getParams(q)};
18083 options.add = true;
18084 options.params.start = this.page * this.pageSize;
18087 this.store.load(options);
18090 * this code will make the page width larger, at the beginning, the list not align correctly,
18091 * we should expand the list on onLoad
18092 * so command out it
18097 this.selectedIndex = -1;
18102 this.loadNext = false;
18106 getParams : function(q){
18108 //p[this.queryParam] = q;
18112 p.limit = this.pageSize;
18118 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18120 collapse : function(){
18121 if(!this.isExpanded()){
18127 this.hasFocus = false;
18131 this.cancelBtn.hide();
18132 this.trigger.show();
18135 this.tickableInputEl().dom.value = '';
18136 this.tickableInputEl().blur();
18141 Roo.get(document).un('mousedown', this.collapseIf, this);
18142 Roo.get(document).un('mousewheel', this.collapseIf, this);
18143 if (!this.editable) {
18144 Roo.get(document).un('keydown', this.listKeyPress, this);
18146 this.fireEvent('collapse', this);
18152 collapseIf : function(e){
18153 var in_combo = e.within(this.el);
18154 var in_list = e.within(this.list);
18155 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18157 if (in_combo || in_list || is_list) {
18158 //e.stopPropagation();
18163 this.onTickableFooterButtonClick(e, false, false);
18171 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18173 expand : function(){
18175 if(this.isExpanded() || !this.hasFocus){
18179 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18180 this.list.setWidth(lw);
18186 this.restrictHeight();
18190 this.tickItems = Roo.apply([], this.item);
18193 this.cancelBtn.show();
18194 this.trigger.hide();
18197 this.tickableInputEl().focus();
18202 Roo.get(document).on('mousedown', this.collapseIf, this);
18203 Roo.get(document).on('mousewheel', this.collapseIf, this);
18204 if (!this.editable) {
18205 Roo.get(document).on('keydown', this.listKeyPress, this);
18208 this.fireEvent('expand', this);
18212 // Implements the default empty TriggerField.onTriggerClick function
18213 onTriggerClick : function(e)
18215 Roo.log('trigger click');
18217 if(this.disabled || !this.triggerList){
18222 this.loadNext = false;
18224 if(this.isExpanded()){
18226 if (!this.blockFocus) {
18227 this.inputEl().focus();
18231 this.hasFocus = true;
18232 if(this.triggerAction == 'all') {
18233 this.doQuery(this.allQuery, true);
18235 this.doQuery(this.getRawValue());
18237 if (!this.blockFocus) {
18238 this.inputEl().focus();
18243 onTickableTriggerClick : function(e)
18250 this.loadNext = false;
18251 this.hasFocus = true;
18253 if(this.triggerAction == 'all') {
18254 this.doQuery(this.allQuery, true);
18256 this.doQuery(this.getRawValue());
18260 onSearchFieldClick : function(e)
18262 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18263 this.onTickableFooterButtonClick(e, false, false);
18267 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18272 this.loadNext = false;
18273 this.hasFocus = true;
18275 if(this.triggerAction == 'all') {
18276 this.doQuery(this.allQuery, true);
18278 this.doQuery(this.getRawValue());
18282 listKeyPress : function(e)
18284 //Roo.log('listkeypress');
18285 // scroll to first matching element based on key pres..
18286 if (e.isSpecialKey()) {
18289 var k = String.fromCharCode(e.getKey()).toUpperCase();
18292 var csel = this.view.getSelectedNodes();
18293 var cselitem = false;
18295 var ix = this.view.indexOf(csel[0]);
18296 cselitem = this.store.getAt(ix);
18297 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18303 this.store.each(function(v) {
18305 // start at existing selection.
18306 if (cselitem.id == v.id) {
18312 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18313 match = this.store.indexOf(v);
18319 if (match === false) {
18320 return true; // no more action?
18323 this.view.select(match);
18324 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18325 sn.scrollIntoView(sn.dom.parentNode, false);
18328 onViewScroll : function(e, t){
18330 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){
18334 this.hasQuery = true;
18336 this.loading = this.list.select('.loading', true).first();
18338 if(this.loading === null){
18339 this.list.createChild({
18341 cls: 'loading roo-select2-more-results roo-select2-active',
18342 html: 'Loading more results...'
18345 this.loading = this.list.select('.loading', true).first();
18347 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18349 this.loading.hide();
18352 this.loading.show();
18357 this.loadNext = true;
18359 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18364 addItem : function(o)
18366 var dv = ''; // display value
18368 if (this.displayField) {
18369 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18371 // this is an error condition!!!
18372 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18379 var choice = this.choices.createChild({
18381 cls: 'roo-select2-search-choice',
18390 cls: 'roo-select2-search-choice-close fa fa-times',
18395 }, this.searchField);
18397 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18399 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18407 this.inputEl().dom.value = '';
18412 onRemoveItem : function(e, _self, o)
18414 e.preventDefault();
18416 this.lastItem = Roo.apply([], this.item);
18418 var index = this.item.indexOf(o.data) * 1;
18421 Roo.log('not this item?!');
18425 this.item.splice(index, 1);
18430 this.fireEvent('remove', this, e);
18436 syncValue : function()
18438 if(!this.item.length){
18445 Roo.each(this.item, function(i){
18446 if(_this.valueField){
18447 value.push(i[_this.valueField]);
18454 this.value = value.join(',');
18456 if(this.hiddenField){
18457 this.hiddenField.dom.value = this.value;
18460 this.store.fireEvent("datachanged", this.store);
18465 clearItem : function()
18467 if(!this.multiple){
18473 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18481 if(this.tickable && !Roo.isTouch){
18482 this.view.refresh();
18486 inputEl: function ()
18488 if(Roo.isIOS && this.useNativeIOS){
18489 return this.el.select('select.roo-ios-select', true).first();
18492 if(Roo.isTouch && this.mobileTouchView){
18493 return this.el.select('input.form-control',true).first();
18497 return this.searchField;
18500 return this.el.select('input.form-control',true).first();
18503 onTickableFooterButtonClick : function(e, btn, el)
18505 e.preventDefault();
18507 this.lastItem = Roo.apply([], this.item);
18509 if(btn && btn.name == 'cancel'){
18510 this.tickItems = Roo.apply([], this.item);
18519 Roo.each(this.tickItems, function(o){
18527 validate : function()
18529 if(this.getVisibilityEl().hasClass('hidden')){
18533 var v = this.getRawValue();
18536 v = this.getValue();
18539 if(this.disabled || this.allowBlank || v.length){
18544 this.markInvalid();
18548 tickableInputEl : function()
18550 if(!this.tickable || !this.editable){
18551 return this.inputEl();
18554 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18558 getAutoCreateTouchView : function()
18563 cls: 'form-group' //input-group
18569 type : this.inputType,
18570 cls : 'form-control x-combo-noedit',
18571 autocomplete: 'new-password',
18572 placeholder : this.placeholder || '',
18577 input.name = this.name;
18581 input.cls += ' input-' + this.size;
18584 if (this.disabled) {
18585 input.disabled = true;
18589 cls : 'roo-combobox-wrap',
18596 inputblock.cls += ' input-group';
18598 inputblock.cn.unshift({
18600 cls : 'input-group-addon input-group-prepend input-group-text',
18605 if(this.removable && !this.multiple){
18606 inputblock.cls += ' roo-removable';
18608 inputblock.cn.push({
18611 cls : 'roo-combo-removable-btn close'
18615 if(this.hasFeedback && !this.allowBlank){
18617 inputblock.cls += ' has-feedback';
18619 inputblock.cn.push({
18621 cls: 'glyphicon form-control-feedback'
18628 inputblock.cls += (this.before) ? '' : ' input-group';
18630 inputblock.cn.push({
18632 cls : 'input-group-addon input-group-append input-group-text',
18638 var ibwrap = inputblock;
18643 cls: 'roo-select2-choices',
18647 cls: 'roo-select2-search-field',
18660 cls: 'roo-select2-container input-group roo-touchview-combobox ',
18665 cls: 'form-hidden-field'
18671 if(!this.multiple && this.showToggleBtn){
18677 if (this.caret != false) {
18680 cls: 'fa fa-' + this.caret
18687 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18689 Roo.bootstrap.version == 3 ? caret : '',
18692 cls: 'combobox-clear',
18706 combobox.cls += ' roo-select2-container-multi';
18709 var required = this.allowBlank ? {
18711 style: 'display: none'
18714 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18715 tooltip : 'This field is required'
18718 var align = this.labelAlign || this.parentLabelAlign();
18720 if (align ==='left' && this.fieldLabel.length) {
18726 cls : 'control-label col-form-label',
18727 html : this.fieldLabel
18731 cls : 'roo-combobox-wrap ',
18738 var labelCfg = cfg.cn[1];
18739 var contentCfg = cfg.cn[2];
18742 if(this.indicatorpos == 'right'){
18747 cls : 'control-label col-form-label',
18751 html : this.fieldLabel
18757 cls : "roo-combobox-wrap ",
18765 labelCfg = cfg.cn[0];
18766 contentCfg = cfg.cn[1];
18771 if(this.labelWidth > 12){
18772 labelCfg.style = "width: " + this.labelWidth + 'px';
18775 if(this.labelWidth < 13 && this.labelmd == 0){
18776 this.labelmd = this.labelWidth;
18779 if(this.labellg > 0){
18780 labelCfg.cls += ' col-lg-' + this.labellg;
18781 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18784 if(this.labelmd > 0){
18785 labelCfg.cls += ' col-md-' + this.labelmd;
18786 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18789 if(this.labelsm > 0){
18790 labelCfg.cls += ' col-sm-' + this.labelsm;
18791 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18794 if(this.labelxs > 0){
18795 labelCfg.cls += ' col-xs-' + this.labelxs;
18796 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18800 } else if ( this.fieldLabel.length) {
18805 cls : 'control-label',
18806 html : this.fieldLabel
18817 if(this.indicatorpos == 'right'){
18821 cls : 'control-label',
18822 html : this.fieldLabel,
18840 var settings = this;
18842 ['xs','sm','md','lg'].map(function(size){
18843 if (settings[size]) {
18844 cfg.cls += ' col-' + size + '-' + settings[size];
18851 initTouchView : function()
18853 this.renderTouchView();
18855 this.touchViewEl.on('scroll', function(){
18856 this.el.dom.scrollTop = 0;
18859 this.originalValue = this.getValue();
18861 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18863 this.inputEl().on("click", this.showTouchView, this);
18864 if (this.triggerEl) {
18865 this.triggerEl.on("click", this.showTouchView, this);
18869 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18870 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18872 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18874 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18875 this.store.on('load', this.onTouchViewLoad, this);
18876 this.store.on('loadexception', this.onTouchViewLoadException, this);
18878 if(this.hiddenName){
18880 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18882 this.hiddenField.dom.value =
18883 this.hiddenValue !== undefined ? this.hiddenValue :
18884 this.value !== undefined ? this.value : '';
18886 this.el.dom.removeAttribute('name');
18887 this.hiddenField.dom.setAttribute('name', this.hiddenName);
18891 this.choices = this.el.select('ul.roo-select2-choices', true).first();
18892 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18895 if(this.removable && !this.multiple){
18896 var close = this.closeTriggerEl();
18898 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18899 close.on('click', this.removeBtnClick, this, close);
18903 * fix the bug in Safari iOS8
18905 this.inputEl().on("focus", function(e){
18906 document.activeElement.blur();
18909 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18916 renderTouchView : function()
18918 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18919 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18921 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18922 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18924 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18925 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18926 this.touchViewBodyEl.setStyle('overflow', 'auto');
18928 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18929 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18931 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18932 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18936 showTouchView : function()
18942 this.touchViewHeaderEl.hide();
18944 if(this.modalTitle.length){
18945 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18946 this.touchViewHeaderEl.show();
18949 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18950 this.touchViewEl.show();
18952 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18954 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18955 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18957 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18959 if(this.modalTitle.length){
18960 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18963 this.touchViewBodyEl.setHeight(bodyHeight);
18967 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18969 this.touchViewEl.addClass(['in','show']);
18972 if(this._touchViewMask){
18973 Roo.get(document.body).addClass("x-body-masked");
18974 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18975 this._touchViewMask.setStyle('z-index', 10000);
18976 this._touchViewMask.addClass('show');
18979 this.doTouchViewQuery();
18983 hideTouchView : function()
18985 this.touchViewEl.removeClass(['in','show']);
18989 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18991 this.touchViewEl.setStyle('display', 'none');
18994 if(this._touchViewMask){
18995 this._touchViewMask.removeClass('show');
18996 Roo.get(document.body).removeClass("x-body-masked");
19000 setTouchViewValue : function()
19007 Roo.each(this.tickItems, function(o){
19012 this.hideTouchView();
19015 doTouchViewQuery : function()
19024 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19028 if(!this.alwaysQuery || this.mode == 'local'){
19029 this.onTouchViewLoad();
19036 onTouchViewBeforeLoad : function(combo,opts)
19042 onTouchViewLoad : function()
19044 if(this.store.getCount() < 1){
19045 this.onTouchViewEmptyResults();
19049 this.clearTouchView();
19051 var rawValue = this.getRawValue();
19053 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19055 this.tickItems = [];
19057 this.store.data.each(function(d, rowIndex){
19058 var row = this.touchViewListGroup.createChild(template);
19060 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19061 row.addClass(d.data.cls);
19064 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19067 html : d.data[this.displayField]
19070 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19071 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19074 row.removeClass('selected');
19075 if(!this.multiple && this.valueField &&
19076 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19079 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19080 row.addClass('selected');
19083 if(this.multiple && this.valueField &&
19084 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19088 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19089 this.tickItems.push(d.data);
19092 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19096 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19098 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19100 if(this.modalTitle.length){
19101 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19104 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19106 if(this.mobile_restrict_height && listHeight < bodyHeight){
19107 this.touchViewBodyEl.setHeight(listHeight);
19112 if(firstChecked && listHeight > bodyHeight){
19113 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19118 onTouchViewLoadException : function()
19120 this.hideTouchView();
19123 onTouchViewEmptyResults : function()
19125 this.clearTouchView();
19127 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19129 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19133 clearTouchView : function()
19135 this.touchViewListGroup.dom.innerHTML = '';
19138 onTouchViewClick : function(e, el, o)
19140 e.preventDefault();
19143 var rowIndex = o.rowIndex;
19145 var r = this.store.getAt(rowIndex);
19147 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19149 if(!this.multiple){
19150 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19151 c.dom.removeAttribute('checked');
19154 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19156 this.setFromData(r.data);
19158 var close = this.closeTriggerEl();
19164 this.hideTouchView();
19166 this.fireEvent('select', this, r, rowIndex);
19171 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19172 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19173 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19177 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19178 this.addItem(r.data);
19179 this.tickItems.push(r.data);
19183 getAutoCreateNativeIOS : function()
19186 cls: 'form-group' //input-group,
19191 cls : 'roo-ios-select'
19195 combobox.name = this.name;
19198 if (this.disabled) {
19199 combobox.disabled = true;
19202 var settings = this;
19204 ['xs','sm','md','lg'].map(function(size){
19205 if (settings[size]) {
19206 cfg.cls += ' col-' + size + '-' + settings[size];
19216 initIOSView : function()
19218 this.store.on('load', this.onIOSViewLoad, this);
19223 onIOSViewLoad : function()
19225 if(this.store.getCount() < 1){
19229 this.clearIOSView();
19231 if(this.allowBlank) {
19233 var default_text = '-- SELECT --';
19235 if(this.placeholder.length){
19236 default_text = this.placeholder;
19239 if(this.emptyTitle.length){
19240 default_text += ' - ' + this.emptyTitle + ' -';
19243 var opt = this.inputEl().createChild({
19246 html : default_text
19250 o[this.valueField] = 0;
19251 o[this.displayField] = default_text;
19253 this.ios_options.push({
19260 this.store.data.each(function(d, rowIndex){
19264 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19265 html = d.data[this.displayField];
19270 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19271 value = d.data[this.valueField];
19280 if(this.value == d.data[this.valueField]){
19281 option['selected'] = true;
19284 var opt = this.inputEl().createChild(option);
19286 this.ios_options.push({
19293 this.inputEl().on('change', function(){
19294 this.fireEvent('select', this);
19299 clearIOSView: function()
19301 this.inputEl().dom.innerHTML = '';
19303 this.ios_options = [];
19306 setIOSValue: function(v)
19310 if(!this.ios_options){
19314 Roo.each(this.ios_options, function(opts){
19316 opts.el.dom.removeAttribute('selected');
19318 if(opts.data[this.valueField] != v){
19322 opts.el.dom.setAttribute('selected', true);
19328 * @cfg {Boolean} grow
19332 * @cfg {Number} growMin
19336 * @cfg {Number} growMax
19345 Roo.apply(Roo.bootstrap.ComboBox, {
19349 cls: 'modal-header',
19371 cls: 'list-group-item',
19375 cls: 'roo-combobox-list-group-item-value'
19379 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19393 listItemCheckbox : {
19395 cls: 'list-group-item',
19399 cls: 'roo-combobox-list-group-item-value'
19403 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19419 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19424 cls: 'modal-footer',
19432 cls: 'col-xs-6 text-left',
19435 cls: 'btn btn-danger roo-touch-view-cancel',
19441 cls: 'col-xs-6 text-right',
19444 cls: 'btn btn-success roo-touch-view-ok',
19455 Roo.apply(Roo.bootstrap.ComboBox, {
19457 touchViewTemplate : {
19459 cls: 'modal fade roo-combobox-touch-view',
19463 cls: 'modal-dialog',
19464 style : 'position:fixed', // we have to fix position....
19468 cls: 'modal-content',
19470 Roo.bootstrap.ComboBox.header,
19471 Roo.bootstrap.ComboBox.body,
19472 Roo.bootstrap.ComboBox.footer
19481 * Ext JS Library 1.1.1
19482 * Copyright(c) 2006-2007, Ext JS, LLC.
19484 * Originally Released Under LGPL - original licence link has changed is not relivant.
19487 * <script type="text/javascript">
19492 * @extends Roo.util.Observable
19493 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19494 * This class also supports single and multi selection modes. <br>
19495 * Create a data model bound view:
19497 var store = new Roo.data.Store(...);
19499 var view = new Roo.View({
19501 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19503 singleSelect: true,
19504 selectedClass: "ydataview-selected",
19508 // listen for node click?
19509 view.on("click", function(vw, index, node, e){
19510 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19514 dataModel.load("foobar.xml");
19516 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19518 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19519 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19521 * Note: old style constructor is still suported (container, template, config)
19524 * Create a new View
19525 * @param {Object} config The config object
19528 Roo.View = function(config, depreciated_tpl, depreciated_config){
19530 this.parent = false;
19532 if (typeof(depreciated_tpl) == 'undefined') {
19533 // new way.. - universal constructor.
19534 Roo.apply(this, config);
19535 this.el = Roo.get(this.el);
19538 this.el = Roo.get(config);
19539 this.tpl = depreciated_tpl;
19540 Roo.apply(this, depreciated_config);
19542 this.wrapEl = this.el.wrap().wrap();
19543 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19546 if(typeof(this.tpl) == "string"){
19547 this.tpl = new Roo.Template(this.tpl);
19549 // support xtype ctors..
19550 this.tpl = new Roo.factory(this.tpl, Roo);
19554 this.tpl.compile();
19559 * @event beforeclick
19560 * Fires before a click is processed. Returns false to cancel the default action.
19561 * @param {Roo.View} this
19562 * @param {Number} index The index of the target node
19563 * @param {HTMLElement} node The target node
19564 * @param {Roo.EventObject} e The raw event object
19566 "beforeclick" : true,
19569 * Fires when a template node is clicked.
19570 * @param {Roo.View} this
19571 * @param {Number} index The index of the target node
19572 * @param {HTMLElement} node The target node
19573 * @param {Roo.EventObject} e The raw event object
19578 * Fires when a template node is double clicked.
19579 * @param {Roo.View} this
19580 * @param {Number} index The index of the target node
19581 * @param {HTMLElement} node The target node
19582 * @param {Roo.EventObject} e The raw event object
19586 * @event contextmenu
19587 * Fires when a template node is right clicked.
19588 * @param {Roo.View} this
19589 * @param {Number} index The index of the target node
19590 * @param {HTMLElement} node The target node
19591 * @param {Roo.EventObject} e The raw event object
19593 "contextmenu" : true,
19595 * @event selectionchange
19596 * Fires when the selected nodes change.
19597 * @param {Roo.View} this
19598 * @param {Array} selections Array of the selected nodes
19600 "selectionchange" : true,
19603 * @event beforeselect
19604 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19605 * @param {Roo.View} this
19606 * @param {HTMLElement} node The node to be selected
19607 * @param {Array} selections Array of currently selected nodes
19609 "beforeselect" : true,
19611 * @event preparedata
19612 * Fires on every row to render, to allow you to change the data.
19613 * @param {Roo.View} this
19614 * @param {Object} data to be rendered (change this)
19616 "preparedata" : true
19624 "click": this.onClick,
19625 "dblclick": this.onDblClick,
19626 "contextmenu": this.onContextMenu,
19630 this.selections = [];
19632 this.cmp = new Roo.CompositeElementLite([]);
19634 this.store = Roo.factory(this.store, Roo.data);
19635 this.setStore(this.store, true);
19638 if ( this.footer && this.footer.xtype) {
19640 var fctr = this.wrapEl.appendChild(document.createElement("div"));
19642 this.footer.dataSource = this.store;
19643 this.footer.container = fctr;
19644 this.footer = Roo.factory(this.footer, Roo);
19645 fctr.insertFirst(this.el);
19647 // this is a bit insane - as the paging toolbar seems to detach the el..
19648 // dom.parentNode.parentNode.parentNode
19649 // they get detached?
19653 Roo.View.superclass.constructor.call(this);
19658 Roo.extend(Roo.View, Roo.util.Observable, {
19661 * @cfg {Roo.data.Store} store Data store to load data from.
19666 * @cfg {String|Roo.Element} el The container element.
19671 * @cfg {String|Roo.Template} tpl The template used by this View
19675 * @cfg {String} dataName the named area of the template to use as the data area
19676 * Works with domtemplates roo-name="name"
19680 * @cfg {String} selectedClass The css class to add to selected nodes
19682 selectedClass : "x-view-selected",
19684 * @cfg {String} emptyText The empty text to show when nothing is loaded.
19689 * @cfg {String} text to display on mask (default Loading)
19693 * @cfg {Boolean} multiSelect Allow multiple selection
19695 multiSelect : false,
19697 * @cfg {Boolean} singleSelect Allow single selection
19699 singleSelect: false,
19702 * @cfg {Boolean} toggleSelect - selecting
19704 toggleSelect : false,
19707 * @cfg {Boolean} tickable - selecting
19712 * Returns the element this view is bound to.
19713 * @return {Roo.Element}
19715 getEl : function(){
19716 return this.wrapEl;
19722 * Refreshes the view. - called by datachanged on the store. - do not call directly.
19724 refresh : function(){
19725 //Roo.log('refresh');
19728 // if we are using something like 'domtemplate', then
19729 // the what gets used is:
19730 // t.applySubtemplate(NAME, data, wrapping data..)
19731 // the outer template then get' applied with
19732 // the store 'extra data'
19733 // and the body get's added to the
19734 // roo-name="data" node?
19735 // <span class='roo-tpl-{name}'></span> ?????
19739 this.clearSelections();
19740 this.el.update("");
19742 var records = this.store.getRange();
19743 if(records.length < 1) {
19745 // is this valid?? = should it render a template??
19747 this.el.update(this.emptyText);
19751 if (this.dataName) {
19752 this.el.update(t.apply(this.store.meta)); //????
19753 el = this.el.child('.roo-tpl-' + this.dataName);
19756 for(var i = 0, len = records.length; i < len; i++){
19757 var data = this.prepareData(records[i].data, i, records[i]);
19758 this.fireEvent("preparedata", this, data, i, records[i]);
19760 var d = Roo.apply({}, data);
19763 Roo.apply(d, {'roo-id' : Roo.id()});
19767 Roo.each(this.parent.item, function(item){
19768 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19771 Roo.apply(d, {'roo-data-checked' : 'checked'});
19775 html[html.length] = Roo.util.Format.trim(
19777 t.applySubtemplate(this.dataName, d, this.store.meta) :
19784 el.update(html.join(""));
19785 this.nodes = el.dom.childNodes;
19786 this.updateIndexes(0);
19791 * Function to override to reformat the data that is sent to
19792 * the template for each node.
19793 * DEPRICATED - use the preparedata event handler.
19794 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19795 * a JSON object for an UpdateManager bound view).
19797 prepareData : function(data, index, record)
19799 this.fireEvent("preparedata", this, data, index, record);
19803 onUpdate : function(ds, record){
19804 // Roo.log('on update');
19805 this.clearSelections();
19806 var index = this.store.indexOf(record);
19807 var n = this.nodes[index];
19808 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19809 n.parentNode.removeChild(n);
19810 this.updateIndexes(index, index);
19816 onAdd : function(ds, records, index)
19818 //Roo.log(['on Add', ds, records, index] );
19819 this.clearSelections();
19820 if(this.nodes.length == 0){
19824 var n = this.nodes[index];
19825 for(var i = 0, len = records.length; i < len; i++){
19826 var d = this.prepareData(records[i].data, i, records[i]);
19828 this.tpl.insertBefore(n, d);
19831 this.tpl.append(this.el, d);
19834 this.updateIndexes(index);
19837 onRemove : function(ds, record, index){
19838 // Roo.log('onRemove');
19839 this.clearSelections();
19840 var el = this.dataName ?
19841 this.el.child('.roo-tpl-' + this.dataName) :
19844 el.dom.removeChild(this.nodes[index]);
19845 this.updateIndexes(index);
19849 * Refresh an individual node.
19850 * @param {Number} index
19852 refreshNode : function(index){
19853 this.onUpdate(this.store, this.store.getAt(index));
19856 updateIndexes : function(startIndex, endIndex){
19857 var ns = this.nodes;
19858 startIndex = startIndex || 0;
19859 endIndex = endIndex || ns.length - 1;
19860 for(var i = startIndex; i <= endIndex; i++){
19861 ns[i].nodeIndex = i;
19866 * Changes the data store this view uses and refresh the view.
19867 * @param {Store} store
19869 setStore : function(store, initial){
19870 if(!initial && this.store){
19871 this.store.un("datachanged", this.refresh);
19872 this.store.un("add", this.onAdd);
19873 this.store.un("remove", this.onRemove);
19874 this.store.un("update", this.onUpdate);
19875 this.store.un("clear", this.refresh);
19876 this.store.un("beforeload", this.onBeforeLoad);
19877 this.store.un("load", this.onLoad);
19878 this.store.un("loadexception", this.onLoad);
19882 store.on("datachanged", this.refresh, this);
19883 store.on("add", this.onAdd, this);
19884 store.on("remove", this.onRemove, this);
19885 store.on("update", this.onUpdate, this);
19886 store.on("clear", this.refresh, this);
19887 store.on("beforeload", this.onBeforeLoad, this);
19888 store.on("load", this.onLoad, this);
19889 store.on("loadexception", this.onLoad, this);
19897 * onbeforeLoad - masks the loading area.
19900 onBeforeLoad : function(store,opts)
19902 //Roo.log('onBeforeLoad');
19904 this.el.update("");
19906 this.el.mask(this.mask ? this.mask : "Loading" );
19908 onLoad : function ()
19915 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19916 * @param {HTMLElement} node
19917 * @return {HTMLElement} The template node
19919 findItemFromChild : function(node){
19920 var el = this.dataName ?
19921 this.el.child('.roo-tpl-' + this.dataName,true) :
19924 if(!node || node.parentNode == el){
19927 var p = node.parentNode;
19928 while(p && p != el){
19929 if(p.parentNode == el){
19938 onClick : function(e){
19939 var item = this.findItemFromChild(e.getTarget());
19941 var index = this.indexOf(item);
19942 if(this.onItemClick(item, index, e) !== false){
19943 this.fireEvent("click", this, index, item, e);
19946 this.clearSelections();
19951 onContextMenu : function(e){
19952 var item = this.findItemFromChild(e.getTarget());
19954 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19959 onDblClick : function(e){
19960 var item = this.findItemFromChild(e.getTarget());
19962 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19966 onItemClick : function(item, index, e)
19968 if(this.fireEvent("beforeclick", this, index, item, e) === false){
19971 if (this.toggleSelect) {
19972 var m = this.isSelected(item) ? 'unselect' : 'select';
19975 _t[m](item, true, false);
19978 if(this.multiSelect || this.singleSelect){
19979 if(this.multiSelect && e.shiftKey && this.lastSelection){
19980 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19982 this.select(item, this.multiSelect && e.ctrlKey);
19983 this.lastSelection = item;
19986 if(!this.tickable){
19987 e.preventDefault();
19995 * Get the number of selected nodes.
19998 getSelectionCount : function(){
19999 return this.selections.length;
20003 * Get the currently selected nodes.
20004 * @return {Array} An array of HTMLElements
20006 getSelectedNodes : function(){
20007 return this.selections;
20011 * Get the indexes of the selected nodes.
20014 getSelectedIndexes : function(){
20015 var indexes = [], s = this.selections;
20016 for(var i = 0, len = s.length; i < len; i++){
20017 indexes.push(s[i].nodeIndex);
20023 * Clear all selections
20024 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20026 clearSelections : function(suppressEvent){
20027 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20028 this.cmp.elements = this.selections;
20029 this.cmp.removeClass(this.selectedClass);
20030 this.selections = [];
20031 if(!suppressEvent){
20032 this.fireEvent("selectionchange", this, this.selections);
20038 * Returns true if the passed node is selected
20039 * @param {HTMLElement/Number} node The node or node index
20040 * @return {Boolean}
20042 isSelected : function(node){
20043 var s = this.selections;
20047 node = this.getNode(node);
20048 return s.indexOf(node) !== -1;
20053 * @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
20054 * @param {Boolean} keepExisting (optional) true to keep existing selections
20055 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20057 select : function(nodeInfo, keepExisting, suppressEvent){
20058 if(nodeInfo instanceof Array){
20060 this.clearSelections(true);
20062 for(var i = 0, len = nodeInfo.length; i < len; i++){
20063 this.select(nodeInfo[i], true, true);
20067 var node = this.getNode(nodeInfo);
20068 if(!node || this.isSelected(node)){
20069 return; // already selected.
20072 this.clearSelections(true);
20075 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20076 Roo.fly(node).addClass(this.selectedClass);
20077 this.selections.push(node);
20078 if(!suppressEvent){
20079 this.fireEvent("selectionchange", this, this.selections);
20087 * @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
20088 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20089 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20091 unselect : function(nodeInfo, keepExisting, suppressEvent)
20093 if(nodeInfo instanceof Array){
20094 Roo.each(this.selections, function(s) {
20095 this.unselect(s, nodeInfo);
20099 var node = this.getNode(nodeInfo);
20100 if(!node || !this.isSelected(node)){
20101 //Roo.log("not selected");
20102 return; // not selected.
20106 Roo.each(this.selections, function(s) {
20108 Roo.fly(node).removeClass(this.selectedClass);
20115 this.selections= ns;
20116 this.fireEvent("selectionchange", this, this.selections);
20120 * Gets a template node.
20121 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20122 * @return {HTMLElement} The node or null if it wasn't found
20124 getNode : function(nodeInfo){
20125 if(typeof nodeInfo == "string"){
20126 return document.getElementById(nodeInfo);
20127 }else if(typeof nodeInfo == "number"){
20128 return this.nodes[nodeInfo];
20134 * Gets a range template nodes.
20135 * @param {Number} startIndex
20136 * @param {Number} endIndex
20137 * @return {Array} An array of nodes
20139 getNodes : function(start, end){
20140 var ns = this.nodes;
20141 start = start || 0;
20142 end = typeof end == "undefined" ? ns.length - 1 : end;
20145 for(var i = start; i <= end; i++){
20149 for(var i = start; i >= end; i--){
20157 * Finds the index of the passed node
20158 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20159 * @return {Number} The index of the node or -1
20161 indexOf : function(node){
20162 node = this.getNode(node);
20163 if(typeof node.nodeIndex == "number"){
20164 return node.nodeIndex;
20166 var ns = this.nodes;
20167 for(var i = 0, len = ns.length; i < len; i++){
20178 * based on jquery fullcalendar
20182 Roo.bootstrap = Roo.bootstrap || {};
20184 * @class Roo.bootstrap.Calendar
20185 * @extends Roo.bootstrap.Component
20186 * Bootstrap Calendar class
20187 * @cfg {Boolean} loadMask (true|false) default false
20188 * @cfg {Object} header generate the user specific header of the calendar, default false
20191 * Create a new Container
20192 * @param {Object} config The config object
20197 Roo.bootstrap.Calendar = function(config){
20198 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20202 * Fires when a date is selected
20203 * @param {DatePicker} this
20204 * @param {Date} date The selected date
20208 * @event monthchange
20209 * Fires when the displayed month changes
20210 * @param {DatePicker} this
20211 * @param {Date} date The selected month
20213 'monthchange': true,
20215 * @event evententer
20216 * Fires when mouse over an event
20217 * @param {Calendar} this
20218 * @param {event} Event
20220 'evententer': true,
20222 * @event eventleave
20223 * Fires when the mouse leaves an
20224 * @param {Calendar} this
20227 'eventleave': true,
20229 * @event eventclick
20230 * Fires when the mouse click an
20231 * @param {Calendar} this
20240 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20243 * @cfg {Roo.data.Store} store
20244 * The data source for the calendar
20248 * @cfg {Number} startDay
20249 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20257 getAutoCreate : function(){
20260 var fc_button = function(name, corner, style, content ) {
20261 return Roo.apply({},{
20263 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20265 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20268 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20279 style : 'width:100%',
20286 cls : 'fc-header-left',
20288 fc_button('prev', 'left', 'arrow', '‹' ),
20289 fc_button('next', 'right', 'arrow', '›' ),
20290 { tag: 'span', cls: 'fc-header-space' },
20291 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20299 cls : 'fc-header-center',
20303 cls: 'fc-header-title',
20306 html : 'month / year'
20314 cls : 'fc-header-right',
20316 /* fc_button('month', 'left', '', 'month' ),
20317 fc_button('week', '', '', 'week' ),
20318 fc_button('day', 'right', '', 'day' )
20330 header = this.header;
20333 var cal_heads = function() {
20335 // fixme - handle this.
20337 for (var i =0; i < Date.dayNames.length; i++) {
20338 var d = Date.dayNames[i];
20341 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20342 html : d.substring(0,3)
20346 ret[0].cls += ' fc-first';
20347 ret[6].cls += ' fc-last';
20350 var cal_cell = function(n) {
20353 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20358 cls: 'fc-day-number',
20362 cls: 'fc-day-content',
20366 style: 'position: relative;' // height: 17px;
20378 var cal_rows = function() {
20381 for (var r = 0; r < 6; r++) {
20388 for (var i =0; i < Date.dayNames.length; i++) {
20389 var d = Date.dayNames[i];
20390 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20393 row.cn[0].cls+=' fc-first';
20394 row.cn[0].cn[0].style = 'min-height:90px';
20395 row.cn[6].cls+=' fc-last';
20399 ret[0].cls += ' fc-first';
20400 ret[4].cls += ' fc-prev-last';
20401 ret[5].cls += ' fc-last';
20408 cls: 'fc-border-separate',
20409 style : 'width:100%',
20417 cls : 'fc-first fc-last',
20435 cls : 'fc-content',
20436 style : "position: relative;",
20439 cls : 'fc-view fc-view-month fc-grid',
20440 style : 'position: relative',
20441 unselectable : 'on',
20444 cls : 'fc-event-container',
20445 style : 'position:absolute;z-index:8;top:0;left:0;'
20463 initEvents : function()
20466 throw "can not find store for calendar";
20472 style: "text-align:center",
20476 style: "background-color:white;width:50%;margin:250 auto",
20480 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20491 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20493 var size = this.el.select('.fc-content', true).first().getSize();
20494 this.maskEl.setSize(size.width, size.height);
20495 this.maskEl.enableDisplayMode("block");
20496 if(!this.loadMask){
20497 this.maskEl.hide();
20500 this.store = Roo.factory(this.store, Roo.data);
20501 this.store.on('load', this.onLoad, this);
20502 this.store.on('beforeload', this.onBeforeLoad, this);
20506 this.cells = this.el.select('.fc-day',true);
20507 //Roo.log(this.cells);
20508 this.textNodes = this.el.query('.fc-day-number');
20509 this.cells.addClassOnOver('fc-state-hover');
20511 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20512 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20513 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20514 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20516 this.on('monthchange', this.onMonthChange, this);
20518 this.update(new Date().clearTime());
20521 resize : function() {
20522 var sz = this.el.getSize();
20524 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20525 this.el.select('.fc-day-content div',true).setHeight(34);
20530 showPrevMonth : function(e){
20531 this.update(this.activeDate.add("mo", -1));
20533 showToday : function(e){
20534 this.update(new Date().clearTime());
20537 showNextMonth : function(e){
20538 this.update(this.activeDate.add("mo", 1));
20542 showPrevYear : function(){
20543 this.update(this.activeDate.add("y", -1));
20547 showNextYear : function(){
20548 this.update(this.activeDate.add("y", 1));
20553 update : function(date)
20555 var vd = this.activeDate;
20556 this.activeDate = date;
20557 // if(vd && this.el){
20558 // var t = date.getTime();
20559 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20560 // Roo.log('using add remove');
20562 // this.fireEvent('monthchange', this, date);
20564 // this.cells.removeClass("fc-state-highlight");
20565 // this.cells.each(function(c){
20566 // if(c.dateValue == t){
20567 // c.addClass("fc-state-highlight");
20568 // setTimeout(function(){
20569 // try{c.dom.firstChild.focus();}catch(e){}
20579 var days = date.getDaysInMonth();
20581 var firstOfMonth = date.getFirstDateOfMonth();
20582 var startingPos = firstOfMonth.getDay()-this.startDay;
20584 if(startingPos < this.startDay){
20588 var pm = date.add(Date.MONTH, -1);
20589 var prevStart = pm.getDaysInMonth()-startingPos;
20591 this.cells = this.el.select('.fc-day',true);
20592 this.textNodes = this.el.query('.fc-day-number');
20593 this.cells.addClassOnOver('fc-state-hover');
20595 var cells = this.cells.elements;
20596 var textEls = this.textNodes;
20598 Roo.each(cells, function(cell){
20599 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20602 days += startingPos;
20604 // convert everything to numbers so it's fast
20605 var day = 86400000;
20606 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20609 //Roo.log(prevStart);
20611 var today = new Date().clearTime().getTime();
20612 var sel = date.clearTime().getTime();
20613 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20614 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20615 var ddMatch = this.disabledDatesRE;
20616 var ddText = this.disabledDatesText;
20617 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20618 var ddaysText = this.disabledDaysText;
20619 var format = this.format;
20621 var setCellClass = function(cal, cell){
20625 //Roo.log('set Cell Class');
20627 var t = d.getTime();
20631 cell.dateValue = t;
20633 cell.className += " fc-today";
20634 cell.className += " fc-state-highlight";
20635 cell.title = cal.todayText;
20638 // disable highlight in other month..
20639 //cell.className += " fc-state-highlight";
20644 cell.className = " fc-state-disabled";
20645 cell.title = cal.minText;
20649 cell.className = " fc-state-disabled";
20650 cell.title = cal.maxText;
20654 if(ddays.indexOf(d.getDay()) != -1){
20655 cell.title = ddaysText;
20656 cell.className = " fc-state-disabled";
20659 if(ddMatch && format){
20660 var fvalue = d.dateFormat(format);
20661 if(ddMatch.test(fvalue)){
20662 cell.title = ddText.replace("%0", fvalue);
20663 cell.className = " fc-state-disabled";
20667 if (!cell.initialClassName) {
20668 cell.initialClassName = cell.dom.className;
20671 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20676 for(; i < startingPos; i++) {
20677 textEls[i].innerHTML = (++prevStart);
20678 d.setDate(d.getDate()+1);
20680 cells[i].className = "fc-past fc-other-month";
20681 setCellClass(this, cells[i]);
20686 for(; i < days; i++){
20687 intDay = i - startingPos + 1;
20688 textEls[i].innerHTML = (intDay);
20689 d.setDate(d.getDate()+1);
20691 cells[i].className = ''; // "x-date-active";
20692 setCellClass(this, cells[i]);
20696 for(; i < 42; i++) {
20697 textEls[i].innerHTML = (++extraDays);
20698 d.setDate(d.getDate()+1);
20700 cells[i].className = "fc-future fc-other-month";
20701 setCellClass(this, cells[i]);
20704 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20706 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20708 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20709 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20711 if(totalRows != 6){
20712 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20713 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20716 this.fireEvent('monthchange', this, date);
20720 if(!this.internalRender){
20721 var main = this.el.dom.firstChild;
20722 var w = main.offsetWidth;
20723 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20724 Roo.fly(main).setWidth(w);
20725 this.internalRender = true;
20726 // opera does not respect the auto grow header center column
20727 // then, after it gets a width opera refuses to recalculate
20728 // without a second pass
20729 if(Roo.isOpera && !this.secondPass){
20730 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20731 this.secondPass = true;
20732 this.update.defer(10, this, [date]);
20739 findCell : function(dt) {
20740 dt = dt.clearTime().getTime();
20742 this.cells.each(function(c){
20743 //Roo.log("check " +c.dateValue + '?=' + dt);
20744 if(c.dateValue == dt){
20754 findCells : function(ev) {
20755 var s = ev.start.clone().clearTime().getTime();
20757 var e= ev.end.clone().clearTime().getTime();
20760 this.cells.each(function(c){
20761 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20763 if(c.dateValue > e){
20766 if(c.dateValue < s){
20775 // findBestRow: function(cells)
20779 // for (var i =0 ; i < cells.length;i++) {
20780 // ret = Math.max(cells[i].rows || 0,ret);
20787 addItem : function(ev)
20789 // look for vertical location slot in
20790 var cells = this.findCells(ev);
20792 // ev.row = this.findBestRow(cells);
20794 // work out the location.
20798 for(var i =0; i < cells.length; i++) {
20800 cells[i].row = cells[0].row;
20803 cells[i].row = cells[i].row + 1;
20813 if (crow.start.getY() == cells[i].getY()) {
20815 crow.end = cells[i];
20832 cells[0].events.push(ev);
20834 this.calevents.push(ev);
20837 clearEvents: function() {
20839 if(!this.calevents){
20843 Roo.each(this.cells.elements, function(c){
20849 Roo.each(this.calevents, function(e) {
20850 Roo.each(e.els, function(el) {
20851 el.un('mouseenter' ,this.onEventEnter, this);
20852 el.un('mouseleave' ,this.onEventLeave, this);
20857 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20863 renderEvents: function()
20867 this.cells.each(function(c) {
20876 if(c.row != c.events.length){
20877 r = 4 - (4 - (c.row - c.events.length));
20880 c.events = ev.slice(0, r);
20881 c.more = ev.slice(r);
20883 if(c.more.length && c.more.length == 1){
20884 c.events.push(c.more.pop());
20887 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20891 this.cells.each(function(c) {
20893 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20896 for (var e = 0; e < c.events.length; e++){
20897 var ev = c.events[e];
20898 var rows = ev.rows;
20900 for(var i = 0; i < rows.length; i++) {
20902 // how many rows should it span..
20905 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20906 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20908 unselectable : "on",
20911 cls: 'fc-event-inner',
20915 // cls: 'fc-event-time',
20916 // html : cells.length > 1 ? '' : ev.time
20920 cls: 'fc-event-title',
20921 html : String.format('{0}', ev.title)
20928 cls: 'ui-resizable-handle ui-resizable-e',
20929 html : '  '
20936 cfg.cls += ' fc-event-start';
20938 if ((i+1) == rows.length) {
20939 cfg.cls += ' fc-event-end';
20942 var ctr = _this.el.select('.fc-event-container',true).first();
20943 var cg = ctr.createChild(cfg);
20945 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20946 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20948 var r = (c.more.length) ? 1 : 0;
20949 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20950 cg.setWidth(ebox.right - sbox.x -2);
20952 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20953 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20954 cg.on('click', _this.onEventClick, _this, ev);
20965 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20966 style : 'position: absolute',
20967 unselectable : "on",
20970 cls: 'fc-event-inner',
20974 cls: 'fc-event-title',
20982 cls: 'ui-resizable-handle ui-resizable-e',
20983 html : '  '
20989 var ctr = _this.el.select('.fc-event-container',true).first();
20990 var cg = ctr.createChild(cfg);
20992 var sbox = c.select('.fc-day-content',true).first().getBox();
20993 var ebox = c.select('.fc-day-content',true).first().getBox();
20995 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
20996 cg.setWidth(ebox.right - sbox.x -2);
20998 cg.on('click', _this.onMoreEventClick, _this, c.more);
21008 onEventEnter: function (e, el,event,d) {
21009 this.fireEvent('evententer', this, el, event);
21012 onEventLeave: function (e, el,event,d) {
21013 this.fireEvent('eventleave', this, el, event);
21016 onEventClick: function (e, el,event,d) {
21017 this.fireEvent('eventclick', this, el, event);
21020 onMonthChange: function () {
21024 onMoreEventClick: function(e, el, more)
21028 this.calpopover.placement = 'right';
21029 this.calpopover.setTitle('More');
21031 this.calpopover.setContent('');
21033 var ctr = this.calpopover.el.select('.popover-content', true).first();
21035 Roo.each(more, function(m){
21037 cls : 'fc-event-hori fc-event-draggable',
21040 var cg = ctr.createChild(cfg);
21042 cg.on('click', _this.onEventClick, _this, m);
21045 this.calpopover.show(el);
21050 onLoad: function ()
21052 this.calevents = [];
21055 if(this.store.getCount() > 0){
21056 this.store.data.each(function(d){
21059 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21060 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21061 time : d.data.start_time,
21062 title : d.data.title,
21063 description : d.data.description,
21064 venue : d.data.venue
21069 this.renderEvents();
21071 if(this.calevents.length && this.loadMask){
21072 this.maskEl.hide();
21076 onBeforeLoad: function()
21078 this.clearEvents();
21080 this.maskEl.show();
21094 * @class Roo.bootstrap.Popover
21095 * @extends Roo.bootstrap.Component
21097 * @children Roo.bootstrap.Component
21098 * Bootstrap Popover class
21099 * @cfg {String} html contents of the popover (or false to use children..)
21100 * @cfg {String} title of popover (or false to hide)
21101 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21102 * @cfg {String} trigger click || hover (or false to trigger manually)
21103 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21104 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21105 * - if false and it has a 'parent' then it will be automatically added to that element
21106 * - if string - Roo.get will be called
21107 * @cfg {Number} delay - delay before showing
21110 * Create a new Popover
21111 * @param {Object} config The config object
21114 Roo.bootstrap.Popover = function(config){
21115 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21121 * After the popover show
21123 * @param {Roo.bootstrap.Popover} this
21128 * After the popover hide
21130 * @param {Roo.bootstrap.Popover} this
21136 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21141 placement : 'right',
21142 trigger : 'hover', // hover
21148 can_build_overlaid : false,
21150 maskEl : false, // the mask element
21153 alignEl : false, // when show is called with an element - this get's stored.
21155 getChildContainer : function()
21157 return this.contentEl;
21160 getPopoverHeader : function()
21162 this.title = true; // flag not to hide it..
21163 this.headerEl.addClass('p-0');
21164 return this.headerEl
21168 getAutoCreate : function(){
21171 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21172 style: 'display:block',
21178 cls : 'popover-inner ',
21182 cls: 'popover-title popover-header',
21183 html : this.title === false ? '' : this.title
21186 cls : 'popover-content popover-body ' + (this.cls || ''),
21187 html : this.html || ''
21198 * @param {string} the title
21200 setTitle: function(str)
21204 this.headerEl.dom.innerHTML = str;
21209 * @param {string} the body content
21211 setContent: function(str)
21214 if (this.contentEl) {
21215 this.contentEl.dom.innerHTML = str;
21219 // as it get's added to the bottom of the page.
21220 onRender : function(ct, position)
21222 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21227 var cfg = Roo.apply({}, this.getAutoCreate());
21231 cfg.cls += ' ' + this.cls;
21234 cfg.style = this.style;
21236 //Roo.log("adding to ");
21237 this.el = Roo.get(document.body).createChild(cfg, position);
21238 // Roo.log(this.el);
21241 this.contentEl = this.el.select('.popover-content',true).first();
21242 this.headerEl = this.el.select('.popover-title',true).first();
21245 if(typeof(this.items) != 'undefined'){
21246 var items = this.items;
21249 for(var i =0;i < items.length;i++) {
21250 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21254 this.items = nitems;
21256 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21257 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21264 resizeMask : function()
21266 this.maskEl.setSize(
21267 Roo.lib.Dom.getViewWidth(true),
21268 Roo.lib.Dom.getViewHeight(true)
21272 initEvents : function()
21276 Roo.bootstrap.Popover.register(this);
21279 this.arrowEl = this.el.select('.arrow',true).first();
21280 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21281 this.el.enableDisplayMode('block');
21285 if (this.over === false && !this.parent()) {
21288 if (this.triggers === false) {
21293 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21294 var triggers = this.trigger ? this.trigger.split(' ') : [];
21295 Roo.each(triggers, function(trigger) {
21297 if (trigger == 'click') {
21298 on_el.on('click', this.toggle, this);
21299 } else if (trigger != 'manual') {
21300 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21301 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21303 on_el.on(eventIn ,this.enter, this);
21304 on_el.on(eventOut, this.leave, this);
21314 toggle : function () {
21315 this.hoverState == 'in' ? this.leave() : this.enter();
21318 enter : function () {
21320 clearTimeout(this.timeout);
21322 this.hoverState = 'in';
21324 if (!this.delay || !this.delay.show) {
21329 this.timeout = setTimeout(function () {
21330 if (_t.hoverState == 'in') {
21333 }, this.delay.show)
21336 leave : function() {
21337 clearTimeout(this.timeout);
21339 this.hoverState = 'out';
21341 if (!this.delay || !this.delay.hide) {
21346 this.timeout = setTimeout(function () {
21347 if (_t.hoverState == 'out') {
21350 }, this.delay.hide)
21354 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21355 * @param {string} (left|right|top|bottom) position
21357 show : function (on_el, placement)
21359 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21360 on_el = on_el || false; // default to false
21363 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21364 on_el = this.parent().el;
21365 } else if (this.over) {
21366 on_el = Roo.get(this.over);
21371 this.alignEl = Roo.get( on_el );
21374 this.render(document.body);
21380 if (this.title === false) {
21381 this.headerEl.hide();
21386 this.el.dom.style.display = 'block';
21389 if (this.alignEl) {
21390 this.updatePosition(this.placement, true);
21393 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21394 var es = this.el.getSize();
21395 var x = Roo.lib.Dom.getViewWidth()/2;
21396 var y = Roo.lib.Dom.getViewHeight()/2;
21397 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21402 //var arrow = this.el.select('.arrow',true).first();
21403 //arrow.set(align[2],
21405 this.el.addClass('in');
21409 this.hoverState = 'in';
21412 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21413 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21414 this.maskEl.dom.style.display = 'block';
21415 this.maskEl.addClass('show');
21417 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21419 this.fireEvent('show', this);
21423 * fire this manually after loading a grid in the table for example
21424 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21425 * @param {Boolean} try and move it if we cant get right position.
21427 updatePosition : function(placement, try_move)
21429 // allow for calling with no parameters
21430 placement = placement ? placement : this.placement;
21431 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21433 this.el.removeClass([
21434 'fade','top','bottom', 'left', 'right','in',
21435 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21437 this.el.addClass(placement + ' bs-popover-' + placement);
21439 if (!this.alignEl ) {
21443 switch (placement) {
21445 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21446 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21447 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21448 //normal display... or moved up/down.
21449 this.el.setXY(offset);
21450 var xy = this.alignEl.getAnchorXY('tr', false);
21452 this.arrowEl.setXY(xy);
21455 // continue through...
21456 return this.updatePosition('left', false);
21460 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21461 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21462 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21463 //normal display... or moved up/down.
21464 this.el.setXY(offset);
21465 var xy = this.alignEl.getAnchorXY('tl', false);
21466 xy[0]-=10;xy[1]+=5; // << fix me
21467 this.arrowEl.setXY(xy);
21471 return this.updatePosition('right', false);
21474 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21475 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21476 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21477 //normal display... or moved up/down.
21478 this.el.setXY(offset);
21479 var xy = this.alignEl.getAnchorXY('t', false);
21480 xy[1]-=10; // << fix me
21481 this.arrowEl.setXY(xy);
21485 return this.updatePosition('bottom', false);
21488 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21489 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21490 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21491 //normal display... or moved up/down.
21492 this.el.setXY(offset);
21493 var xy = this.alignEl.getAnchorXY('b', false);
21494 xy[1]+=2; // << fix me
21495 this.arrowEl.setXY(xy);
21499 return this.updatePosition('top', false);
21510 this.el.setXY([0,0]);
21511 this.el.removeClass('in');
21513 this.hoverState = null;
21514 this.maskEl.hide(); // always..
21515 this.fireEvent('hide', this);
21521 Roo.apply(Roo.bootstrap.Popover, {
21524 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21525 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21526 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21527 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21532 clickHander : false,
21536 onMouseDown : function(e)
21538 if (this.popups.length && !e.getTarget(".roo-popover")) {
21539 /// what is nothing is showing..
21548 register : function(popup)
21550 if (!Roo.bootstrap.Popover.clickHandler) {
21551 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21553 // hide other popups.
21554 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21555 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21556 this.hideAll(); //<< why?
21557 //this.popups.push(popup);
21559 hideAll : function()
21561 this.popups.forEach(function(p) {
21565 onShow : function() {
21566 Roo.bootstrap.Popover.popups.push(this);
21568 onHide : function() {
21569 Roo.bootstrap.Popover.popups.remove(this);
21575 * Card header - holder for the card header elements.
21580 * @class Roo.bootstrap.PopoverNav
21581 * @extends Roo.bootstrap.NavGroup
21582 * Bootstrap Popover header navigation class
21584 * Create a new Popover Header Navigation
21585 * @param {Object} config The config object
21588 Roo.bootstrap.PopoverNav = function(config){
21589 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21592 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21595 container_method : 'getPopoverHeader'
21613 * @class Roo.bootstrap.Progress
21614 * @extends Roo.bootstrap.Component
21615 * @children Roo.bootstrap.ProgressBar
21616 * Bootstrap Progress class
21617 * @cfg {Boolean} striped striped of the progress bar
21618 * @cfg {Boolean} active animated of the progress bar
21622 * Create a new Progress
21623 * @param {Object} config The config object
21626 Roo.bootstrap.Progress = function(config){
21627 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21630 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21635 getAutoCreate : function(){
21643 cfg.cls += ' progress-striped';
21647 cfg.cls += ' active';
21666 * @class Roo.bootstrap.ProgressBar
21667 * @extends Roo.bootstrap.Component
21668 * Bootstrap ProgressBar class
21669 * @cfg {Number} aria_valuenow aria-value now
21670 * @cfg {Number} aria_valuemin aria-value min
21671 * @cfg {Number} aria_valuemax aria-value max
21672 * @cfg {String} label label for the progress bar
21673 * @cfg {String} panel (success | info | warning | danger )
21674 * @cfg {String} role role of the progress bar
21675 * @cfg {String} sr_only text
21679 * Create a new ProgressBar
21680 * @param {Object} config The config object
21683 Roo.bootstrap.ProgressBar = function(config){
21684 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21687 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21691 aria_valuemax : 100,
21697 getAutoCreate : function()
21702 cls: 'progress-bar',
21703 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21715 cfg.role = this.role;
21718 if(this.aria_valuenow){
21719 cfg['aria-valuenow'] = this.aria_valuenow;
21722 if(this.aria_valuemin){
21723 cfg['aria-valuemin'] = this.aria_valuemin;
21726 if(this.aria_valuemax){
21727 cfg['aria-valuemax'] = this.aria_valuemax;
21730 if(this.label && !this.sr_only){
21731 cfg.html = this.label;
21735 cfg.cls += ' progress-bar-' + this.panel;
21741 update : function(aria_valuenow)
21743 this.aria_valuenow = aria_valuenow;
21745 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21753 * @class Roo.bootstrap.TabGroup
21754 * @extends Roo.bootstrap.Column
21755 * @children Roo.bootstrap.TabPanel
21756 * Bootstrap Column class
21757 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21758 * @cfg {Boolean} carousel true to make the group behave like a carousel
21759 * @cfg {Boolean} bullets show bullets for the panels
21760 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21761 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21762 * @cfg {Boolean} showarrow (true|false) show arrow default true
21765 * Create a new TabGroup
21766 * @param {Object} config The config object
21769 Roo.bootstrap.TabGroup = function(config){
21770 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21772 this.navId = Roo.id();
21775 Roo.bootstrap.TabGroup.register(this);
21779 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21782 transition : false,
21787 slideOnTouch : false,
21790 getAutoCreate : function()
21792 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21794 cfg.cls += ' tab-content';
21796 if (this.carousel) {
21797 cfg.cls += ' carousel slide';
21800 cls : 'carousel-inner',
21804 if(this.bullets && !Roo.isTouch){
21807 cls : 'carousel-bullets',
21811 if(this.bullets_cls){
21812 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21819 cfg.cn[0].cn.push(bullets);
21822 if(this.showarrow){
21823 cfg.cn[0].cn.push({
21825 class : 'carousel-arrow',
21829 class : 'carousel-prev',
21833 class : 'fa fa-chevron-left'
21839 class : 'carousel-next',
21843 class : 'fa fa-chevron-right'
21856 initEvents: function()
21858 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21859 // this.el.on("touchstart", this.onTouchStart, this);
21862 if(this.autoslide){
21865 this.slideFn = window.setInterval(function() {
21866 _this.showPanelNext();
21870 if(this.showarrow){
21871 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21872 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21878 // onTouchStart : function(e, el, o)
21880 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21884 // this.showPanelNext();
21888 getChildContainer : function()
21890 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21894 * register a Navigation item
21895 * @param {Roo.bootstrap.NavItem} the navitem to add
21897 register : function(item)
21899 this.tabs.push( item);
21900 item.navId = this.navId; // not really needed..
21905 getActivePanel : function()
21908 Roo.each(this.tabs, function(t) {
21918 getPanelByName : function(n)
21921 Roo.each(this.tabs, function(t) {
21922 if (t.tabId == n) {
21930 indexOfPanel : function(p)
21933 Roo.each(this.tabs, function(t,i) {
21934 if (t.tabId == p.tabId) {
21943 * show a specific panel
21944 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21945 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21947 showPanel : function (pan)
21949 if(this.transition || typeof(pan) == 'undefined'){
21950 Roo.log("waiting for the transitionend");
21954 if (typeof(pan) == 'number') {
21955 pan = this.tabs[pan];
21958 if (typeof(pan) == 'string') {
21959 pan = this.getPanelByName(pan);
21962 var cur = this.getActivePanel();
21965 Roo.log('pan or acitve pan is undefined');
21969 if (pan.tabId == this.getActivePanel().tabId) {
21973 if (false === cur.fireEvent('beforedeactivate')) {
21977 if(this.bullets > 0 && !Roo.isTouch){
21978 this.setActiveBullet(this.indexOfPanel(pan));
21981 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21983 //class="carousel-item carousel-item-next carousel-item-left"
21985 this.transition = true;
21986 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
21987 var lr = dir == 'next' ? 'left' : 'right';
21988 pan.el.addClass(dir); // or prev
21989 pan.el.addClass('carousel-item-' + dir); // or prev
21990 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21991 cur.el.addClass(lr); // or right
21992 pan.el.addClass(lr);
21993 cur.el.addClass('carousel-item-' +lr); // or right
21994 pan.el.addClass('carousel-item-' +lr);
21998 cur.el.on('transitionend', function() {
21999 Roo.log("trans end?");
22001 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22002 pan.setActive(true);
22004 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22005 cur.setActive(false);
22007 _this.transition = false;
22009 }, this, { single: true } );
22014 cur.setActive(false);
22015 pan.setActive(true);
22020 showPanelNext : function()
22022 var i = this.indexOfPanel(this.getActivePanel());
22024 if (i >= this.tabs.length - 1 && !this.autoslide) {
22028 if (i >= this.tabs.length - 1 && this.autoslide) {
22032 this.showPanel(this.tabs[i+1]);
22035 showPanelPrev : function()
22037 var i = this.indexOfPanel(this.getActivePanel());
22039 if (i < 1 && !this.autoslide) {
22043 if (i < 1 && this.autoslide) {
22044 i = this.tabs.length;
22047 this.showPanel(this.tabs[i-1]);
22051 addBullet: function()
22053 if(!this.bullets || Roo.isTouch){
22056 var ctr = this.el.select('.carousel-bullets',true).first();
22057 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22058 var bullet = ctr.createChild({
22059 cls : 'bullet bullet-' + i
22060 },ctr.dom.lastChild);
22065 bullet.on('click', (function(e, el, o, ii, t){
22067 e.preventDefault();
22069 this.showPanel(ii);
22071 if(this.autoslide && this.slideFn){
22072 clearInterval(this.slideFn);
22073 this.slideFn = window.setInterval(function() {
22074 _this.showPanelNext();
22078 }).createDelegate(this, [i, bullet], true));
22083 setActiveBullet : function(i)
22089 Roo.each(this.el.select('.bullet', true).elements, function(el){
22090 el.removeClass('selected');
22093 var bullet = this.el.select('.bullet-' + i, true).first();
22099 bullet.addClass('selected');
22110 Roo.apply(Roo.bootstrap.TabGroup, {
22114 * register a Navigation Group
22115 * @param {Roo.bootstrap.NavGroup} the navgroup to add
22117 register : function(navgrp)
22119 this.groups[navgrp.navId] = navgrp;
22123 * fetch a Navigation Group based on the navigation ID
22124 * if one does not exist , it will get created.
22125 * @param {string} the navgroup to add
22126 * @returns {Roo.bootstrap.NavGroup} the navgroup
22128 get: function(navId) {
22129 if (typeof(this.groups[navId]) == 'undefined') {
22130 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22132 return this.groups[navId] ;
22147 * @class Roo.bootstrap.TabPanel
22148 * @extends Roo.bootstrap.Component
22149 * @children Roo.bootstrap.Component
22150 * Bootstrap TabPanel class
22151 * @cfg {Boolean} active panel active
22152 * @cfg {String} html panel content
22153 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22154 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22155 * @cfg {String} href click to link..
22156 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22160 * Create a new TabPanel
22161 * @param {Object} config The config object
22164 Roo.bootstrap.TabPanel = function(config){
22165 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22169 * Fires when the active status changes
22170 * @param {Roo.bootstrap.TabPanel} this
22171 * @param {Boolean} state the new state
22176 * @event beforedeactivate
22177 * Fires before a tab is de-activated - can be used to do validation on a form.
22178 * @param {Roo.bootstrap.TabPanel} this
22179 * @return {Boolean} false if there is an error
22182 'beforedeactivate': true
22185 this.tabId = this.tabId || Roo.id();
22189 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22196 touchSlide : false,
22197 getAutoCreate : function(){
22202 // item is needed for carousel - not sure if it has any effect otherwise
22203 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22204 html: this.html || ''
22208 cfg.cls += ' active';
22212 cfg.tabId = this.tabId;
22220 initEvents: function()
22222 var p = this.parent();
22224 this.navId = this.navId || p.navId;
22226 if (typeof(this.navId) != 'undefined') {
22227 // not really needed.. but just in case.. parent should be a NavGroup.
22228 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22232 var i = tg.tabs.length - 1;
22234 if(this.active && tg.bullets > 0 && i < tg.bullets){
22235 tg.setActiveBullet(i);
22239 this.el.on('click', this.onClick, this);
22241 if(Roo.isTouch && this.touchSlide){
22242 this.el.on("touchstart", this.onTouchStart, this);
22243 this.el.on("touchmove", this.onTouchMove, this);
22244 this.el.on("touchend", this.onTouchEnd, this);
22249 onRender : function(ct, position)
22251 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22254 setActive : function(state)
22256 Roo.log("panel - set active " + this.tabId + "=" + state);
22258 this.active = state;
22260 this.el.removeClass('active');
22262 } else if (!this.el.hasClass('active')) {
22263 this.el.addClass('active');
22266 this.fireEvent('changed', this, state);
22269 onClick : function(e)
22271 e.preventDefault();
22273 if(!this.href.length){
22277 window.location.href = this.href;
22286 onTouchStart : function(e)
22288 this.swiping = false;
22290 this.startX = e.browserEvent.touches[0].clientX;
22291 this.startY = e.browserEvent.touches[0].clientY;
22294 onTouchMove : function(e)
22296 this.swiping = true;
22298 this.endX = e.browserEvent.touches[0].clientX;
22299 this.endY = e.browserEvent.touches[0].clientY;
22302 onTouchEnd : function(e)
22309 var tabGroup = this.parent();
22311 if(this.endX > this.startX){ // swiping right
22312 tabGroup.showPanelPrev();
22316 if(this.startX > this.endX){ // swiping left
22317 tabGroup.showPanelNext();
22336 * @class Roo.bootstrap.DateField
22337 * @extends Roo.bootstrap.Input
22338 * Bootstrap DateField class
22339 * @cfg {Number} weekStart default 0
22340 * @cfg {String} viewMode default empty, (months|years)
22341 * @cfg {String} minViewMode default empty, (months|years)
22342 * @cfg {Number} startDate default -Infinity
22343 * @cfg {Number} endDate default Infinity
22344 * @cfg {Boolean} todayHighlight default false
22345 * @cfg {Boolean} todayBtn default false
22346 * @cfg {Boolean} calendarWeeks default false
22347 * @cfg {Object} daysOfWeekDisabled default empty
22348 * @cfg {Boolean} singleMode default false (true | false)
22350 * @cfg {Boolean} keyboardNavigation default true
22351 * @cfg {String} language default en
22354 * Create a new DateField
22355 * @param {Object} config The config object
22358 Roo.bootstrap.DateField = function(config){
22359 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22363 * Fires when this field show.
22364 * @param {Roo.bootstrap.DateField} this
22365 * @param {Mixed} date The date value
22370 * Fires when this field hide.
22371 * @param {Roo.bootstrap.DateField} this
22372 * @param {Mixed} date The date value
22377 * Fires when select a date.
22378 * @param {Roo.bootstrap.DateField} this
22379 * @param {Mixed} date The date value
22383 * @event beforeselect
22384 * Fires when before select a date.
22385 * @param {Roo.bootstrap.DateField} this
22386 * @param {Mixed} date The date value
22388 beforeselect : true
22392 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22395 * @cfg {String} format
22396 * The default date format string which can be overriden for localization support. The format must be
22397 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22401 * @cfg {String} altFormats
22402 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22403 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22405 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22413 todayHighlight : false,
22419 keyboardNavigation: true,
22421 calendarWeeks: false,
22423 startDate: -Infinity,
22427 daysOfWeekDisabled: [],
22431 singleMode : false,
22433 UTCDate: function()
22435 return new Date(Date.UTC.apply(Date, arguments));
22438 UTCToday: function()
22440 var today = new Date();
22441 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22444 getDate: function() {
22445 var d = this.getUTCDate();
22446 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22449 getUTCDate: function() {
22453 setDate: function(d) {
22454 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22457 setUTCDate: function(d) {
22459 this.setValue(this.formatDate(this.date));
22462 onRender: function(ct, position)
22465 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22467 this.language = this.language || 'en';
22468 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22469 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22471 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22472 this.format = this.format || 'm/d/y';
22473 this.isInline = false;
22474 this.isInput = true;
22475 this.component = this.el.select('.add-on', true).first() || false;
22476 this.component = (this.component && this.component.length === 0) ? false : this.component;
22477 this.hasInput = this.component && this.inputEl().length;
22479 if (typeof(this.minViewMode === 'string')) {
22480 switch (this.minViewMode) {
22482 this.minViewMode = 1;
22485 this.minViewMode = 2;
22488 this.minViewMode = 0;
22493 if (typeof(this.viewMode === 'string')) {
22494 switch (this.viewMode) {
22507 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22509 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22511 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22513 this.picker().on('mousedown', this.onMousedown, this);
22514 this.picker().on('click', this.onClick, this);
22516 this.picker().addClass('datepicker-dropdown');
22518 this.startViewMode = this.viewMode;
22520 if(this.singleMode){
22521 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22522 v.setVisibilityMode(Roo.Element.DISPLAY);
22526 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22527 v.setStyle('width', '189px');
22531 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22532 if(!this.calendarWeeks){
22537 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22538 v.attr('colspan', function(i, val){
22539 return parseInt(val) + 1;
22544 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22546 this.setStartDate(this.startDate);
22547 this.setEndDate(this.endDate);
22549 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22556 if(this.isInline) {
22561 picker : function()
22563 return this.pickerEl;
22564 // return this.el.select('.datepicker', true).first();
22567 fillDow: function()
22569 var dowCnt = this.weekStart;
22578 if(this.calendarWeeks){
22586 while (dowCnt < this.weekStart + 7) {
22590 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22594 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22597 fillMonths: function()
22600 var months = this.picker().select('>.datepicker-months td', true).first();
22602 months.dom.innerHTML = '';
22608 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22611 months.createChild(month);
22618 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;
22620 if (this.date < this.startDate) {
22621 this.viewDate = new Date(this.startDate);
22622 } else if (this.date > this.endDate) {
22623 this.viewDate = new Date(this.endDate);
22625 this.viewDate = new Date(this.date);
22633 var d = new Date(this.viewDate),
22634 year = d.getUTCFullYear(),
22635 month = d.getUTCMonth(),
22636 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22637 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22638 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22639 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22640 currentDate = this.date && this.date.valueOf(),
22641 today = this.UTCToday();
22643 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22645 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22647 // this.picker.select('>tfoot th.today').
22648 // .text(dates[this.language].today)
22649 // .toggle(this.todayBtn !== false);
22651 this.updateNavArrows();
22654 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22656 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22658 prevMonth.setUTCDate(day);
22660 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22662 var nextMonth = new Date(prevMonth);
22664 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22666 nextMonth = nextMonth.valueOf();
22668 var fillMonths = false;
22670 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22672 while(prevMonth.valueOf() <= nextMonth) {
22675 if (prevMonth.getUTCDay() === this.weekStart) {
22677 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22685 if(this.calendarWeeks){
22686 // ISO 8601: First week contains first thursday.
22687 // ISO also states week starts on Monday, but we can be more abstract here.
22689 // Start of current week: based on weekstart/current date
22690 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22691 // Thursday of this week
22692 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22693 // First Thursday of year, year from thursday
22694 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22695 // Calendar week: ms between thursdays, div ms per day, div 7 days
22696 calWeek = (th - yth) / 864e5 / 7 + 1;
22698 fillMonths.cn.push({
22706 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22708 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22711 if (this.todayHighlight &&
22712 prevMonth.getUTCFullYear() == today.getFullYear() &&
22713 prevMonth.getUTCMonth() == today.getMonth() &&
22714 prevMonth.getUTCDate() == today.getDate()) {
22715 clsName += ' today';
22718 if (currentDate && prevMonth.valueOf() === currentDate) {
22719 clsName += ' active';
22722 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22723 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22724 clsName += ' disabled';
22727 fillMonths.cn.push({
22729 cls: 'day ' + clsName,
22730 html: prevMonth.getDate()
22733 prevMonth.setDate(prevMonth.getDate()+1);
22736 var currentYear = this.date && this.date.getUTCFullYear();
22737 var currentMonth = this.date && this.date.getUTCMonth();
22739 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22741 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22742 v.removeClass('active');
22744 if(currentYear === year && k === currentMonth){
22745 v.addClass('active');
22748 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22749 v.addClass('disabled');
22755 year = parseInt(year/10, 10) * 10;
22757 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22759 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22762 for (var i = -1; i < 11; i++) {
22763 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22765 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22773 showMode: function(dir)
22776 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22779 Roo.each(this.picker().select('>div',true).elements, function(v){
22780 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22783 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22788 if(this.isInline) {
22792 this.picker().removeClass(['bottom', 'top']);
22794 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22796 * place to the top of element!
22800 this.picker().addClass('top');
22801 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22806 this.picker().addClass('bottom');
22808 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22811 parseDate : function(value)
22813 if(!value || value instanceof Date){
22816 var v = Date.parseDate(value, this.format);
22817 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22818 v = Date.parseDate(value, 'Y-m-d');
22820 if(!v && this.altFormats){
22821 if(!this.altFormatsArray){
22822 this.altFormatsArray = this.altFormats.split("|");
22824 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22825 v = Date.parseDate(value, this.altFormatsArray[i]);
22831 formatDate : function(date, fmt)
22833 return (!date || !(date instanceof Date)) ?
22834 date : date.dateFormat(fmt || this.format);
22837 onFocus : function()
22839 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22843 onBlur : function()
22845 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22847 var d = this.inputEl().getValue();
22854 showPopup : function()
22856 this.picker().show();
22860 this.fireEvent('showpopup', this, this.date);
22863 hidePopup : function()
22865 if(this.isInline) {
22868 this.picker().hide();
22869 this.viewMode = this.startViewMode;
22872 this.fireEvent('hidepopup', this, this.date);
22876 onMousedown: function(e)
22878 e.stopPropagation();
22879 e.preventDefault();
22884 Roo.bootstrap.DateField.superclass.keyup.call(this);
22888 setValue: function(v)
22890 if(this.fireEvent('beforeselect', this, v) !== false){
22891 var d = new Date(this.parseDate(v) ).clearTime();
22893 if(isNaN(d.getTime())){
22894 this.date = this.viewDate = '';
22895 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22899 v = this.formatDate(d);
22901 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22903 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22907 this.fireEvent('select', this, this.date);
22911 getValue: function()
22913 return this.formatDate(this.date);
22916 fireKey: function(e)
22918 if (!this.picker().isVisible()){
22919 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22925 var dateChanged = false,
22927 newDate, newViewDate;
22932 e.preventDefault();
22936 if (!this.keyboardNavigation) {
22939 dir = e.keyCode == 37 ? -1 : 1;
22942 newDate = this.moveYear(this.date, dir);
22943 newViewDate = this.moveYear(this.viewDate, dir);
22944 } else if (e.shiftKey){
22945 newDate = this.moveMonth(this.date, dir);
22946 newViewDate = this.moveMonth(this.viewDate, dir);
22948 newDate = new Date(this.date);
22949 newDate.setUTCDate(this.date.getUTCDate() + dir);
22950 newViewDate = new Date(this.viewDate);
22951 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22953 if (this.dateWithinRange(newDate)){
22954 this.date = newDate;
22955 this.viewDate = newViewDate;
22956 this.setValue(this.formatDate(this.date));
22958 e.preventDefault();
22959 dateChanged = true;
22964 if (!this.keyboardNavigation) {
22967 dir = e.keyCode == 38 ? -1 : 1;
22969 newDate = this.moveYear(this.date, dir);
22970 newViewDate = this.moveYear(this.viewDate, dir);
22971 } else if (e.shiftKey){
22972 newDate = this.moveMonth(this.date, dir);
22973 newViewDate = this.moveMonth(this.viewDate, dir);
22975 newDate = new Date(this.date);
22976 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22977 newViewDate = new Date(this.viewDate);
22978 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22980 if (this.dateWithinRange(newDate)){
22981 this.date = newDate;
22982 this.viewDate = newViewDate;
22983 this.setValue(this.formatDate(this.date));
22985 e.preventDefault();
22986 dateChanged = true;
22990 this.setValue(this.formatDate(this.date));
22992 e.preventDefault();
22995 this.setValue(this.formatDate(this.date));
23009 onClick: function(e)
23011 e.stopPropagation();
23012 e.preventDefault();
23014 var target = e.getTarget();
23016 if(target.nodeName.toLowerCase() === 'i'){
23017 target = Roo.get(target).dom.parentNode;
23020 var nodeName = target.nodeName;
23021 var className = target.className;
23022 var html = target.innerHTML;
23023 //Roo.log(nodeName);
23025 switch(nodeName.toLowerCase()) {
23027 switch(className) {
23033 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23034 switch(this.viewMode){
23036 this.viewDate = this.moveMonth(this.viewDate, dir);
23040 this.viewDate = this.moveYear(this.viewDate, dir);
23046 var date = new Date();
23047 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23049 this.setValue(this.formatDate(this.date));
23056 if (className.indexOf('disabled') < 0) {
23057 if (!this.viewDate) {
23058 this.viewDate = new Date();
23060 this.viewDate.setUTCDate(1);
23061 if (className.indexOf('month') > -1) {
23062 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23064 var year = parseInt(html, 10) || 0;
23065 this.viewDate.setUTCFullYear(year);
23069 if(this.singleMode){
23070 this.setValue(this.formatDate(this.viewDate));
23081 //Roo.log(className);
23082 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23083 var day = parseInt(html, 10) || 1;
23084 var year = (this.viewDate || new Date()).getUTCFullYear(),
23085 month = (this.viewDate || new Date()).getUTCMonth();
23087 if (className.indexOf('old') > -1) {
23094 } else if (className.indexOf('new') > -1) {
23102 //Roo.log([year,month,day]);
23103 this.date = this.UTCDate(year, month, day,0,0,0,0);
23104 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23106 //Roo.log(this.formatDate(this.date));
23107 this.setValue(this.formatDate(this.date));
23114 setStartDate: function(startDate)
23116 this.startDate = startDate || -Infinity;
23117 if (this.startDate !== -Infinity) {
23118 this.startDate = this.parseDate(this.startDate);
23121 this.updateNavArrows();
23124 setEndDate: function(endDate)
23126 this.endDate = endDate || Infinity;
23127 if (this.endDate !== Infinity) {
23128 this.endDate = this.parseDate(this.endDate);
23131 this.updateNavArrows();
23134 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23136 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23137 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23138 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23140 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23141 return parseInt(d, 10);
23144 this.updateNavArrows();
23147 updateNavArrows: function()
23149 if(this.singleMode){
23153 var d = new Date(this.viewDate),
23154 year = d.getUTCFullYear(),
23155 month = d.getUTCMonth();
23157 Roo.each(this.picker().select('.prev', true).elements, function(v){
23159 switch (this.viewMode) {
23162 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23168 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23175 Roo.each(this.picker().select('.next', true).elements, function(v){
23177 switch (this.viewMode) {
23180 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23186 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23194 moveMonth: function(date, dir)
23199 var new_date = new Date(date.valueOf()),
23200 day = new_date.getUTCDate(),
23201 month = new_date.getUTCMonth(),
23202 mag = Math.abs(dir),
23204 dir = dir > 0 ? 1 : -1;
23207 // If going back one month, make sure month is not current month
23208 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23210 return new_date.getUTCMonth() == month;
23212 // If going forward one month, make sure month is as expected
23213 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23215 return new_date.getUTCMonth() != new_month;
23217 new_month = month + dir;
23218 new_date.setUTCMonth(new_month);
23219 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23220 if (new_month < 0 || new_month > 11) {
23221 new_month = (new_month + 12) % 12;
23224 // For magnitudes >1, move one month at a time...
23225 for (var i=0; i<mag; i++) {
23226 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23227 new_date = this.moveMonth(new_date, dir);
23229 // ...then reset the day, keeping it in the new month
23230 new_month = new_date.getUTCMonth();
23231 new_date.setUTCDate(day);
23233 return new_month != new_date.getUTCMonth();
23236 // Common date-resetting loop -- if date is beyond end of month, make it
23239 new_date.setUTCDate(--day);
23240 new_date.setUTCMonth(new_month);
23245 moveYear: function(date, dir)
23247 return this.moveMonth(date, dir*12);
23250 dateWithinRange: function(date)
23252 return date >= this.startDate && date <= this.endDate;
23258 this.picker().remove();
23261 validateValue : function(value)
23263 if(this.getVisibilityEl().hasClass('hidden')){
23267 if(value.length < 1) {
23268 if(this.allowBlank){
23274 if(value.length < this.minLength){
23277 if(value.length > this.maxLength){
23281 var vt = Roo.form.VTypes;
23282 if(!vt[this.vtype](value, this)){
23286 if(typeof this.validator == "function"){
23287 var msg = this.validator(value);
23293 if(this.regex && !this.regex.test(value)){
23297 if(typeof(this.parseDate(value)) == 'undefined'){
23301 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23305 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23315 this.date = this.viewDate = '';
23317 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23322 Roo.apply(Roo.bootstrap.DateField, {
23333 html: '<i class="fa fa-arrow-left"/>'
23343 html: '<i class="fa fa-arrow-right"/>'
23385 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23386 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23387 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23388 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23389 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23402 navFnc: 'FullYear',
23407 navFnc: 'FullYear',
23412 Roo.apply(Roo.bootstrap.DateField, {
23416 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23420 cls: 'datepicker-days',
23424 cls: 'table-condensed',
23426 Roo.bootstrap.DateField.head,
23430 Roo.bootstrap.DateField.footer
23437 cls: 'datepicker-months',
23441 cls: 'table-condensed',
23443 Roo.bootstrap.DateField.head,
23444 Roo.bootstrap.DateField.content,
23445 Roo.bootstrap.DateField.footer
23452 cls: 'datepicker-years',
23456 cls: 'table-condensed',
23458 Roo.bootstrap.DateField.head,
23459 Roo.bootstrap.DateField.content,
23460 Roo.bootstrap.DateField.footer
23479 * @class Roo.bootstrap.TimeField
23480 * @extends Roo.bootstrap.Input
23481 * Bootstrap DateField class
23485 * Create a new TimeField
23486 * @param {Object} config The config object
23489 Roo.bootstrap.TimeField = function(config){
23490 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23494 * Fires when this field show.
23495 * @param {Roo.bootstrap.DateField} thisthis
23496 * @param {Mixed} date The date value
23501 * Fires when this field hide.
23502 * @param {Roo.bootstrap.DateField} this
23503 * @param {Mixed} date The date value
23508 * Fires when select a date.
23509 * @param {Roo.bootstrap.DateField} this
23510 * @param {Mixed} date The date value
23516 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23519 * @cfg {String} format
23520 * The default time format string which can be overriden for localization support. The format must be
23521 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23525 getAutoCreate : function()
23527 this.after = '<i class="fa far fa-clock"></i>';
23528 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23532 onRender: function(ct, position)
23535 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23537 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23539 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23541 this.pop = this.picker().select('>.datepicker-time',true).first();
23542 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23544 this.picker().on('mousedown', this.onMousedown, this);
23545 this.picker().on('click', this.onClick, this);
23547 this.picker().addClass('datepicker-dropdown');
23552 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23553 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23554 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23555 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23556 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23557 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23561 fireKey: function(e){
23562 if (!this.picker().isVisible()){
23563 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23569 e.preventDefault();
23577 this.onTogglePeriod();
23580 this.onIncrementMinutes();
23583 this.onDecrementMinutes();
23592 onClick: function(e) {
23593 e.stopPropagation();
23594 e.preventDefault();
23597 picker : function()
23599 return this.pickerEl;
23602 fillTime: function()
23604 var time = this.pop.select('tbody', true).first();
23606 time.dom.innerHTML = '';
23621 cls: 'hours-up fa fas fa-chevron-up'
23641 cls: 'minutes-up fa fas fa-chevron-up'
23662 cls: 'timepicker-hour',
23677 cls: 'timepicker-minute',
23692 cls: 'btn btn-primary period',
23714 cls: 'hours-down fa fas fa-chevron-down'
23734 cls: 'minutes-down fa fas fa-chevron-down'
23752 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23759 var hours = this.time.getHours();
23760 var minutes = this.time.getMinutes();
23773 hours = hours - 12;
23777 hours = '0' + hours;
23781 minutes = '0' + minutes;
23784 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23785 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23786 this.pop.select('button', true).first().dom.innerHTML = period;
23792 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23794 var cls = ['bottom'];
23796 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23803 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23807 //this.picker().setXY(20000,20000);
23808 this.picker().addClass(cls.join('-'));
23812 Roo.each(cls, function(c){
23817 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23818 //_this.picker().setTop(_this.inputEl().getHeight());
23822 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23824 //_this.picker().setTop(0 - _this.picker().getHeight());
23829 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23833 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23841 onFocus : function()
23843 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23847 onBlur : function()
23849 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23855 this.picker().show();
23860 this.fireEvent('show', this, this.date);
23865 this.picker().hide();
23868 this.fireEvent('hide', this, this.date);
23871 setTime : function()
23874 this.setValue(this.time.format(this.format));
23876 this.fireEvent('select', this, this.date);
23881 onMousedown: function(e){
23882 e.stopPropagation();
23883 e.preventDefault();
23886 onIncrementHours: function()
23888 Roo.log('onIncrementHours');
23889 this.time = this.time.add(Date.HOUR, 1);
23894 onDecrementHours: function()
23896 Roo.log('onDecrementHours');
23897 this.time = this.time.add(Date.HOUR, -1);
23901 onIncrementMinutes: function()
23903 Roo.log('onIncrementMinutes');
23904 this.time = this.time.add(Date.MINUTE, 1);
23908 onDecrementMinutes: function()
23910 Roo.log('onDecrementMinutes');
23911 this.time = this.time.add(Date.MINUTE, -1);
23915 onTogglePeriod: function()
23917 Roo.log('onTogglePeriod');
23918 this.time = this.time.add(Date.HOUR, 12);
23926 Roo.apply(Roo.bootstrap.TimeField, {
23930 cls: 'datepicker dropdown-menu',
23934 cls: 'datepicker-time',
23938 cls: 'table-condensed',
23967 cls: 'btn btn-info ok',
23995 * @class Roo.bootstrap.MonthField
23996 * @extends Roo.bootstrap.Input
23997 * Bootstrap MonthField class
23999 * @cfg {String} language default en
24002 * Create a new MonthField
24003 * @param {Object} config The config object
24006 Roo.bootstrap.MonthField = function(config){
24007 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
24012 * Fires when this field show.
24013 * @param {Roo.bootstrap.MonthField} this
24014 * @param {Mixed} date The date value
24019 * Fires when this field hide.
24020 * @param {Roo.bootstrap.MonthField} this
24021 * @param {Mixed} date The date value
24026 * Fires when select a date.
24027 * @param {Roo.bootstrap.MonthField} this
24028 * @param {String} oldvalue The old value
24029 * @param {String} newvalue The new value
24035 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
24037 onRender: function(ct, position)
24040 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24042 this.language = this.language || 'en';
24043 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24044 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24046 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24047 this.isInline = false;
24048 this.isInput = true;
24049 this.component = this.el.select('.add-on', true).first() || false;
24050 this.component = (this.component && this.component.length === 0) ? false : this.component;
24051 this.hasInput = this.component && this.inputEL().length;
24053 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24055 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24057 this.picker().on('mousedown', this.onMousedown, this);
24058 this.picker().on('click', this.onClick, this);
24060 this.picker().addClass('datepicker-dropdown');
24062 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24063 v.setStyle('width', '189px');
24070 if(this.isInline) {
24076 setValue: function(v, suppressEvent)
24078 var o = this.getValue();
24080 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24084 if(suppressEvent !== true){
24085 this.fireEvent('select', this, o, v);
24090 getValue: function()
24095 onClick: function(e)
24097 e.stopPropagation();
24098 e.preventDefault();
24100 var target = e.getTarget();
24102 if(target.nodeName.toLowerCase() === 'i'){
24103 target = Roo.get(target).dom.parentNode;
24106 var nodeName = target.nodeName;
24107 var className = target.className;
24108 var html = target.innerHTML;
24110 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24114 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24116 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24122 picker : function()
24124 return this.pickerEl;
24127 fillMonths: function()
24130 var months = this.picker().select('>.datepicker-months td', true).first();
24132 months.dom.innerHTML = '';
24138 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24141 months.createChild(month);
24150 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24151 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24154 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24155 e.removeClass('active');
24157 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24158 e.addClass('active');
24165 if(this.isInline) {
24169 this.picker().removeClass(['bottom', 'top']);
24171 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24173 * place to the top of element!
24177 this.picker().addClass('top');
24178 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24183 this.picker().addClass('bottom');
24185 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24188 onFocus : function()
24190 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24194 onBlur : function()
24196 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24198 var d = this.inputEl().getValue();
24207 this.picker().show();
24208 this.picker().select('>.datepicker-months', true).first().show();
24212 this.fireEvent('show', this, this.date);
24217 if(this.isInline) {
24220 this.picker().hide();
24221 this.fireEvent('hide', this, this.date);
24225 onMousedown: function(e)
24227 e.stopPropagation();
24228 e.preventDefault();
24233 Roo.bootstrap.MonthField.superclass.keyup.call(this);
24237 fireKey: function(e)
24239 if (!this.picker().isVisible()){
24240 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24251 e.preventDefault();
24255 dir = e.keyCode == 37 ? -1 : 1;
24257 this.vIndex = this.vIndex + dir;
24259 if(this.vIndex < 0){
24263 if(this.vIndex > 11){
24267 if(isNaN(this.vIndex)){
24271 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24277 dir = e.keyCode == 38 ? -1 : 1;
24279 this.vIndex = this.vIndex + dir * 4;
24281 if(this.vIndex < 0){
24285 if(this.vIndex > 11){
24289 if(isNaN(this.vIndex)){
24293 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24298 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24299 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24303 e.preventDefault();
24306 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24307 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24323 this.picker().remove();
24328 Roo.apply(Roo.bootstrap.MonthField, {
24347 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24348 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24353 Roo.apply(Roo.bootstrap.MonthField, {
24357 cls: 'datepicker dropdown-menu roo-dynamic',
24361 cls: 'datepicker-months',
24365 cls: 'table-condensed',
24367 Roo.bootstrap.DateField.content
24387 * @class Roo.bootstrap.CheckBox
24388 * @extends Roo.bootstrap.Input
24389 * Bootstrap CheckBox class
24391 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24392 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24393 * @cfg {String} boxLabel The text that appears beside the checkbox
24394 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24395 * @cfg {Boolean} checked initnal the element
24396 * @cfg {Boolean} inline inline the element (default false)
24397 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24398 * @cfg {String} tooltip label tooltip
24401 * Create a new CheckBox
24402 * @param {Object} config The config object
24405 Roo.bootstrap.CheckBox = function(config){
24406 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24411 * Fires when the element is checked or unchecked.
24412 * @param {Roo.bootstrap.CheckBox} this This input
24413 * @param {Boolean} checked The new checked value
24418 * Fires when the element is click.
24419 * @param {Roo.bootstrap.CheckBox} this This input
24426 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24428 inputType: 'checkbox',
24437 // checkbox success does not make any sense really..
24442 getAutoCreate : function()
24444 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24450 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24453 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24459 type : this.inputType,
24460 value : this.inputValue,
24461 cls : 'roo-' + this.inputType, //'form-box',
24462 placeholder : this.placeholder || ''
24466 if(this.inputType != 'radio'){
24470 cls : 'roo-hidden-value',
24471 value : this.checked ? this.inputValue : this.valueOff
24476 if (this.weight) { // Validity check?
24477 cfg.cls += " " + this.inputType + "-" + this.weight;
24480 if (this.disabled) {
24481 input.disabled=true;
24485 input.checked = this.checked;
24490 input.name = this.name;
24492 if(this.inputType != 'radio'){
24493 hidden.name = this.name;
24494 input.name = '_hidden_' + this.name;
24499 input.cls += ' input-' + this.size;
24504 ['xs','sm','md','lg'].map(function(size){
24505 if (settings[size]) {
24506 cfg.cls += ' col-' + size + '-' + settings[size];
24510 var inputblock = input;
24512 if (this.before || this.after) {
24515 cls : 'input-group',
24520 inputblock.cn.push({
24522 cls : 'input-group-addon',
24527 inputblock.cn.push(input);
24529 if(this.inputType != 'radio'){
24530 inputblock.cn.push(hidden);
24534 inputblock.cn.push({
24536 cls : 'input-group-addon',
24542 var boxLabelCfg = false;
24548 //'for': id, // box label is handled by onclick - so no for...
24550 html: this.boxLabel
24553 boxLabelCfg.tooltip = this.tooltip;
24559 if (align ==='left' && this.fieldLabel.length) {
24560 // Roo.log("left and has label");
24565 cls : 'control-label',
24566 html : this.fieldLabel
24577 cfg.cn[1].cn.push(boxLabelCfg);
24580 if(this.labelWidth > 12){
24581 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24584 if(this.labelWidth < 13 && this.labelmd == 0){
24585 this.labelmd = this.labelWidth;
24588 if(this.labellg > 0){
24589 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24590 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24593 if(this.labelmd > 0){
24594 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24595 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24598 if(this.labelsm > 0){
24599 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24600 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24603 if(this.labelxs > 0){
24604 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24605 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24608 } else if ( this.fieldLabel.length) {
24609 // Roo.log(" label");
24613 tag: this.boxLabel ? 'span' : 'label',
24615 cls: 'control-label box-input-label',
24616 //cls : 'input-group-addon',
24617 html : this.fieldLabel
24624 cfg.cn.push(boxLabelCfg);
24629 // Roo.log(" no label && no align");
24630 cfg.cn = [ inputblock ] ;
24632 cfg.cn.push(boxLabelCfg);
24640 if(this.inputType != 'radio'){
24641 cfg.cn.push(hidden);
24649 * return the real input element.
24651 inputEl: function ()
24653 return this.el.select('input.roo-' + this.inputType,true).first();
24655 hiddenEl: function ()
24657 return this.el.select('input.roo-hidden-value',true).first();
24660 labelEl: function()
24662 return this.el.select('label.control-label',true).first();
24664 /* depricated... */
24668 return this.labelEl();
24671 boxLabelEl: function()
24673 return this.el.select('label.box-label',true).first();
24676 initEvents : function()
24678 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24680 this.inputEl().on('click', this.onClick, this);
24682 if (this.boxLabel) {
24683 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24686 this.startValue = this.getValue();
24689 Roo.bootstrap.CheckBox.register(this);
24693 onClick : function(e)
24695 if(this.fireEvent('click', this, e) !== false){
24696 this.setChecked(!this.checked);
24701 setChecked : function(state,suppressEvent)
24703 this.startValue = this.getValue();
24705 if(this.inputType == 'radio'){
24707 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24708 e.dom.checked = false;
24711 this.inputEl().dom.checked = true;
24713 this.inputEl().dom.value = this.inputValue;
24715 if(suppressEvent !== true){
24716 this.fireEvent('check', this, true);
24724 this.checked = state;
24726 this.inputEl().dom.checked = state;
24729 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24731 if(suppressEvent !== true){
24732 this.fireEvent('check', this, state);
24738 getValue : function()
24740 if(this.inputType == 'radio'){
24741 return this.getGroupValue();
24744 return this.hiddenEl().dom.value;
24748 getGroupValue : function()
24750 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24754 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24757 setValue : function(v,suppressEvent)
24759 if(this.inputType == 'radio'){
24760 this.setGroupValue(v, suppressEvent);
24764 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24769 setGroupValue : function(v, suppressEvent)
24771 this.startValue = this.getValue();
24773 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24774 e.dom.checked = false;
24776 if(e.dom.value == v){
24777 e.dom.checked = true;
24781 if(suppressEvent !== true){
24782 this.fireEvent('check', this, true);
24790 validate : function()
24792 if(this.getVisibilityEl().hasClass('hidden')){
24798 (this.inputType == 'radio' && this.validateRadio()) ||
24799 (this.inputType == 'checkbox' && this.validateCheckbox())
24805 this.markInvalid();
24809 validateRadio : function()
24811 if(this.getVisibilityEl().hasClass('hidden')){
24815 if(this.allowBlank){
24821 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24822 if(!e.dom.checked){
24834 validateCheckbox : function()
24837 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24838 //return (this.getValue() == this.inputValue) ? true : false;
24841 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24849 for(var i in group){
24850 if(group[i].el.isVisible(true)){
24858 for(var i in group){
24863 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24870 * Mark this field as valid
24872 markValid : function()
24876 this.fireEvent('valid', this);
24878 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24881 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24888 if(this.inputType == 'radio'){
24889 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24890 var fg = e.findParent('.form-group', false, true);
24891 if (Roo.bootstrap.version == 3) {
24892 fg.removeClass([_this.invalidClass, _this.validClass]);
24893 fg.addClass(_this.validClass);
24895 fg.removeClass(['is-valid', 'is-invalid']);
24896 fg.addClass('is-valid');
24904 var fg = this.el.findParent('.form-group', false, true);
24905 if (Roo.bootstrap.version == 3) {
24906 fg.removeClass([this.invalidClass, this.validClass]);
24907 fg.addClass(this.validClass);
24909 fg.removeClass(['is-valid', 'is-invalid']);
24910 fg.addClass('is-valid');
24915 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24921 for(var i in group){
24922 var fg = group[i].el.findParent('.form-group', false, true);
24923 if (Roo.bootstrap.version == 3) {
24924 fg.removeClass([this.invalidClass, this.validClass]);
24925 fg.addClass(this.validClass);
24927 fg.removeClass(['is-valid', 'is-invalid']);
24928 fg.addClass('is-valid');
24934 * Mark this field as invalid
24935 * @param {String} msg The validation message
24937 markInvalid : function(msg)
24939 if(this.allowBlank){
24945 this.fireEvent('invalid', this, msg);
24947 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24950 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24954 label.markInvalid();
24957 if(this.inputType == 'radio'){
24959 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24960 var fg = e.findParent('.form-group', false, true);
24961 if (Roo.bootstrap.version == 3) {
24962 fg.removeClass([_this.invalidClass, _this.validClass]);
24963 fg.addClass(_this.invalidClass);
24965 fg.removeClass(['is-invalid', 'is-valid']);
24966 fg.addClass('is-invalid');
24974 var fg = this.el.findParent('.form-group', false, true);
24975 if (Roo.bootstrap.version == 3) {
24976 fg.removeClass([_this.invalidClass, _this.validClass]);
24977 fg.addClass(_this.invalidClass);
24979 fg.removeClass(['is-invalid', 'is-valid']);
24980 fg.addClass('is-invalid');
24985 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24991 for(var i in group){
24992 var fg = group[i].el.findParent('.form-group', false, true);
24993 if (Roo.bootstrap.version == 3) {
24994 fg.removeClass([_this.invalidClass, _this.validClass]);
24995 fg.addClass(_this.invalidClass);
24997 fg.removeClass(['is-invalid', 'is-valid']);
24998 fg.addClass('is-invalid');
25004 clearInvalid : function()
25006 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
25008 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25010 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
25012 if (label && label.iconEl) {
25013 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25014 label.iconEl.removeClass(['is-invalid', 'is-valid']);
25018 disable : function()
25020 if(this.inputType != 'radio'){
25021 Roo.bootstrap.CheckBox.superclass.disable.call(this);
25028 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25029 _this.getActionEl().addClass(this.disabledClass);
25030 e.dom.disabled = true;
25034 this.disabled = true;
25035 this.fireEvent("disable", this);
25039 enable : function()
25041 if(this.inputType != 'radio'){
25042 Roo.bootstrap.CheckBox.superclass.enable.call(this);
25049 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25050 _this.getActionEl().removeClass(this.disabledClass);
25051 e.dom.disabled = false;
25055 this.disabled = false;
25056 this.fireEvent("enable", this);
25060 setBoxLabel : function(v)
25065 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25071 Roo.apply(Roo.bootstrap.CheckBox, {
25076 * register a CheckBox Group
25077 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25079 register : function(checkbox)
25081 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25082 this.groups[checkbox.groupId] = {};
25085 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25089 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25093 * fetch a CheckBox Group based on the group ID
25094 * @param {string} the group ID
25095 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25097 get: function(groupId) {
25098 if (typeof(this.groups[groupId]) == 'undefined') {
25102 return this.groups[groupId] ;
25115 * @class Roo.bootstrap.Radio
25116 * @extends Roo.bootstrap.Component
25117 * Bootstrap Radio class
25118 * @cfg {String} boxLabel - the label associated
25119 * @cfg {String} value - the value of radio
25122 * Create a new Radio
25123 * @param {Object} config The config object
25125 Roo.bootstrap.Radio = function(config){
25126 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25130 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25136 getAutoCreate : function()
25140 cls : 'form-group radio',
25145 html : this.boxLabel
25153 initEvents : function()
25155 this.parent().register(this);
25157 this.el.on('click', this.onClick, this);
25161 onClick : function(e)
25163 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25164 this.setChecked(true);
25168 setChecked : function(state, suppressEvent)
25170 this.parent().setValue(this.value, suppressEvent);
25174 setBoxLabel : function(v)
25179 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25194 * @class Roo.bootstrap.SecurePass
25195 * @extends Roo.bootstrap.Input
25196 * Bootstrap SecurePass class
25200 * Create a new SecurePass
25201 * @param {Object} config The config object
25204 Roo.bootstrap.SecurePass = function (config) {
25205 // these go here, so the translation tool can replace them..
25207 PwdEmpty: "Please type a password, and then retype it to confirm.",
25208 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25209 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25210 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25211 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25212 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25213 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25214 TooWeak: "Your password is Too Weak."
25216 this.meterLabel = "Password strength:";
25217 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25218 this.meterClass = [
25219 "roo-password-meter-tooweak",
25220 "roo-password-meter-weak",
25221 "roo-password-meter-medium",
25222 "roo-password-meter-strong",
25223 "roo-password-meter-grey"
25228 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25231 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25233 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25235 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25236 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25237 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25238 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25239 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25240 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25241 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25251 * @cfg {String/Object} Label for the strength meter (defaults to
25252 * 'Password strength:')
25257 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25258 * ['Weak', 'Medium', 'Strong'])
25261 pwdStrengths: false,
25274 initEvents: function ()
25276 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25278 if (this.el.is('input[type=password]') && Roo.isSafari) {
25279 this.el.on('keydown', this.SafariOnKeyDown, this);
25282 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25285 onRender: function (ct, position)
25287 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25288 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25289 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25291 this.trigger.createChild({
25296 cls: 'roo-password-meter-grey col-xs-12',
25299 //width: this.meterWidth + 'px'
25303 cls: 'roo-password-meter-text'
25309 if (this.hideTrigger) {
25310 this.trigger.setDisplayed(false);
25312 this.setSize(this.width || '', this.height || '');
25315 onDestroy: function ()
25317 if (this.trigger) {
25318 this.trigger.removeAllListeners();
25319 this.trigger.remove();
25322 this.wrap.remove();
25324 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25327 checkStrength: function ()
25329 var pwd = this.inputEl().getValue();
25330 if (pwd == this._lastPwd) {
25335 if (this.ClientSideStrongPassword(pwd)) {
25337 } else if (this.ClientSideMediumPassword(pwd)) {
25339 } else if (this.ClientSideWeakPassword(pwd)) {
25345 Roo.log('strength1: ' + strength);
25347 //var pm = this.trigger.child('div/div/div').dom;
25348 var pm = this.trigger.child('div/div');
25349 pm.removeClass(this.meterClass);
25350 pm.addClass(this.meterClass[strength]);
25353 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25355 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25357 this._lastPwd = pwd;
25361 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25363 this._lastPwd = '';
25365 var pm = this.trigger.child('div/div');
25366 pm.removeClass(this.meterClass);
25367 pm.addClass('roo-password-meter-grey');
25370 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25373 this.inputEl().dom.type='password';
25376 validateValue: function (value)
25378 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25381 if (value.length == 0) {
25382 if (this.allowBlank) {
25383 this.clearInvalid();
25387 this.markInvalid(this.errors.PwdEmpty);
25388 this.errorMsg = this.errors.PwdEmpty;
25396 if (!value.match(/[\x21-\x7e]+/)) {
25397 this.markInvalid(this.errors.PwdBadChar);
25398 this.errorMsg = this.errors.PwdBadChar;
25401 if (value.length < 6) {
25402 this.markInvalid(this.errors.PwdShort);
25403 this.errorMsg = this.errors.PwdShort;
25406 if (value.length > 16) {
25407 this.markInvalid(this.errors.PwdLong);
25408 this.errorMsg = this.errors.PwdLong;
25412 if (this.ClientSideStrongPassword(value)) {
25414 } else if (this.ClientSideMediumPassword(value)) {
25416 } else if (this.ClientSideWeakPassword(value)) {
25423 if (strength < 2) {
25424 //this.markInvalid(this.errors.TooWeak);
25425 this.errorMsg = this.errors.TooWeak;
25430 console.log('strength2: ' + strength);
25432 //var pm = this.trigger.child('div/div/div').dom;
25434 var pm = this.trigger.child('div/div');
25435 pm.removeClass(this.meterClass);
25436 pm.addClass(this.meterClass[strength]);
25438 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25440 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25442 this.errorMsg = '';
25446 CharacterSetChecks: function (type)
25449 this.fResult = false;
25452 isctype: function (character, type)
25455 case this.kCapitalLetter:
25456 if (character >= 'A' && character <= 'Z') {
25461 case this.kSmallLetter:
25462 if (character >= 'a' && character <= 'z') {
25468 if (character >= '0' && character <= '9') {
25473 case this.kPunctuation:
25474 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25485 IsLongEnough: function (pwd, size)
25487 return !(pwd == null || isNaN(size) || pwd.length < size);
25490 SpansEnoughCharacterSets: function (word, nb)
25492 if (!this.IsLongEnough(word, nb))
25497 var characterSetChecks = new Array(
25498 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25499 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25502 for (var index = 0; index < word.length; ++index) {
25503 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25504 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25505 characterSetChecks[nCharSet].fResult = true;
25512 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25513 if (characterSetChecks[nCharSet].fResult) {
25518 if (nCharSets < nb) {
25524 ClientSideStrongPassword: function (pwd)
25526 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25529 ClientSideMediumPassword: function (pwd)
25531 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25534 ClientSideWeakPassword: function (pwd)
25536 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25539 })//<script type="text/javascript">
25542 * Based Ext JS Library 1.1.1
25543 * Copyright(c) 2006-2007, Ext JS, LLC.
25549 * @class Roo.HtmlEditorCore
25550 * @extends Roo.Component
25551 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25553 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25556 Roo.HtmlEditorCore = function(config){
25559 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25564 * @event initialize
25565 * Fires when the editor is fully initialized (including the iframe)
25566 * @param {Roo.HtmlEditorCore} this
25571 * Fires when the editor is first receives the focus. Any insertion must wait
25572 * until after this event.
25573 * @param {Roo.HtmlEditorCore} this
25577 * @event beforesync
25578 * Fires before the textarea is updated with content from the editor iframe. Return false
25579 * to cancel the sync.
25580 * @param {Roo.HtmlEditorCore} this
25581 * @param {String} html
25585 * @event beforepush
25586 * Fires before the iframe editor is updated with content from the textarea. Return false
25587 * to cancel the push.
25588 * @param {Roo.HtmlEditorCore} this
25589 * @param {String} html
25594 * Fires when the textarea is updated with content from the editor iframe.
25595 * @param {Roo.HtmlEditorCore} this
25596 * @param {String} html
25601 * Fires when the iframe editor is updated with content from the textarea.
25602 * @param {Roo.HtmlEditorCore} this
25603 * @param {String} html
25608 * @event editorevent
25609 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25610 * @param {Roo.HtmlEditorCore} this
25616 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25618 // defaults : white / black...
25619 this.applyBlacklists();
25626 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25630 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25636 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25641 * @cfg {Number} height (in pixels)
25645 * @cfg {Number} width (in pixels)
25650 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25653 stylesheets: false,
25656 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25658 allowComments: false,
25662 // private properties
25663 validationEvent : false,
25665 initialized : false,
25667 sourceEditMode : false,
25668 onFocus : Roo.emptyFn,
25670 hideMode:'offsets',
25674 // blacklist + whitelisted elements..
25681 * Protected method that will not generally be called directly. It
25682 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25683 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25685 getDocMarkup : function(){
25689 // inherit styels from page...??
25690 if (this.stylesheets === false) {
25692 Roo.get(document.head).select('style').each(function(node) {
25693 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25696 Roo.get(document.head).select('link').each(function(node) {
25697 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25700 } else if (!this.stylesheets.length) {
25702 st = '<style type="text/css">' +
25703 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25706 for (var i in this.stylesheets) {
25707 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25712 st += '<style type="text/css">' +
25713 'IMG { cursor: pointer } ' +
25716 var cls = 'roo-htmleditor-body';
25718 if(this.bodyCls.length){
25719 cls += ' ' + this.bodyCls;
25722 return '<html><head>' + st +
25723 //<style type="text/css">' +
25724 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25726 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25730 onRender : function(ct, position)
25733 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25734 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25737 this.el.dom.style.border = '0 none';
25738 this.el.dom.setAttribute('tabIndex', -1);
25739 this.el.addClass('x-hidden hide');
25743 if(Roo.isIE){ // fix IE 1px bogus margin
25744 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25748 this.frameId = Roo.id();
25752 var iframe = this.owner.wrap.createChild({
25754 cls: 'form-control', // bootstrap..
25756 name: this.frameId,
25757 frameBorder : 'no',
25758 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25763 this.iframe = iframe.dom;
25765 this.assignDocWin();
25767 this.doc.designMode = 'on';
25770 this.doc.write(this.getDocMarkup());
25774 var task = { // must defer to wait for browser to be ready
25776 //console.log("run task?" + this.doc.readyState);
25777 this.assignDocWin();
25778 if(this.doc.body || this.doc.readyState == 'complete'){
25780 this.doc.designMode="on";
25784 Roo.TaskMgr.stop(task);
25785 this.initEditor.defer(10, this);
25792 Roo.TaskMgr.start(task);
25797 onResize : function(w, h)
25799 Roo.log('resize: ' +w + ',' + h );
25800 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25804 if(typeof w == 'number'){
25806 this.iframe.style.width = w + 'px';
25808 if(typeof h == 'number'){
25810 this.iframe.style.height = h + 'px';
25812 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25819 * Toggles the editor between standard and source edit mode.
25820 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25822 toggleSourceEdit : function(sourceEditMode){
25824 this.sourceEditMode = sourceEditMode === true;
25826 if(this.sourceEditMode){
25828 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25831 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25832 //this.iframe.className = '';
25835 //this.setSize(this.owner.wrap.getSize());
25836 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25843 * Protected method that will not generally be called directly. If you need/want
25844 * custom HTML cleanup, this is the method you should override.
25845 * @param {String} html The HTML to be cleaned
25846 * return {String} The cleaned HTML
25848 cleanHtml : function(html){
25849 html = String(html);
25850 if(html.length > 5){
25851 if(Roo.isSafari){ // strip safari nonsense
25852 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25855 if(html == ' '){
25862 * HTML Editor -> Textarea
25863 * Protected method that will not generally be called directly. Syncs the contents
25864 * of the editor iframe with the textarea.
25866 syncValue : function(){
25867 if(this.initialized){
25868 var bd = (this.doc.body || this.doc.documentElement);
25869 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25870 var html = bd.innerHTML;
25872 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25873 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25875 html = '<div style="'+m[0]+'">' + html + '</div>';
25878 html = this.cleanHtml(html);
25879 // fix up the special chars.. normaly like back quotes in word...
25880 // however we do not want to do this with chinese..
25881 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25883 var cc = match.charCodeAt();
25885 // Get the character value, handling surrogate pairs
25886 if (match.length == 2) {
25887 // It's a surrogate pair, calculate the Unicode code point
25888 var high = match.charCodeAt(0) - 0xD800;
25889 var low = match.charCodeAt(1) - 0xDC00;
25890 cc = (high * 0x400) + low + 0x10000;
25892 (cc >= 0x4E00 && cc < 0xA000 ) ||
25893 (cc >= 0x3400 && cc < 0x4E00 ) ||
25894 (cc >= 0xf900 && cc < 0xfb00 )
25899 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25900 return "&#" + cc + ";";
25907 if(this.owner.fireEvent('beforesync', this, html) !== false){
25908 this.el.dom.value = html;
25909 this.owner.fireEvent('sync', this, html);
25915 * Protected method that will not generally be called directly. Pushes the value of the textarea
25916 * into the iframe editor.
25918 pushValue : function(){
25919 if(this.initialized){
25920 var v = this.el.dom.value.trim();
25922 // if(v.length < 1){
25926 if(this.owner.fireEvent('beforepush', this, v) !== false){
25927 var d = (this.doc.body || this.doc.documentElement);
25929 this.cleanUpPaste();
25930 this.el.dom.value = d.innerHTML;
25931 this.owner.fireEvent('push', this, v);
25937 deferFocus : function(){
25938 this.focus.defer(10, this);
25942 focus : function(){
25943 if(this.win && !this.sourceEditMode){
25950 assignDocWin: function()
25952 var iframe = this.iframe;
25955 this.doc = iframe.contentWindow.document;
25956 this.win = iframe.contentWindow;
25958 // if (!Roo.get(this.frameId)) {
25961 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25962 // this.win = Roo.get(this.frameId).dom.contentWindow;
25964 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25968 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25969 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25974 initEditor : function(){
25975 //console.log("INIT EDITOR");
25976 this.assignDocWin();
25980 this.doc.designMode="on";
25982 this.doc.write(this.getDocMarkup());
25985 var dbody = (this.doc.body || this.doc.documentElement);
25986 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25987 // this copies styles from the containing element into thsi one..
25988 // not sure why we need all of this..
25989 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25991 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25992 //ss['background-attachment'] = 'fixed'; // w3c
25993 dbody.bgProperties = 'fixed'; // ie
25994 //Roo.DomHelper.applyStyles(dbody, ss);
25995 Roo.EventManager.on(this.doc, {
25996 //'mousedown': this.onEditorEvent,
25997 'mouseup': this.onEditorEvent,
25998 'dblclick': this.onEditorEvent,
25999 'click': this.onEditorEvent,
26000 'keyup': this.onEditorEvent,
26005 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26007 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26008 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26010 this.initialized = true;
26012 this.owner.fireEvent('initialize', this);
26017 onDestroy : function(){
26023 //for (var i =0; i < this.toolbars.length;i++) {
26024 // // fixme - ask toolbars for heights?
26025 // this.toolbars[i].onDestroy();
26028 //this.wrap.dom.innerHTML = '';
26029 //this.wrap.remove();
26034 onFirstFocus : function(){
26036 this.assignDocWin();
26039 this.activated = true;
26042 if(Roo.isGecko){ // prevent silly gecko errors
26044 var s = this.win.getSelection();
26045 if(!s.focusNode || s.focusNode.nodeType != 3){
26046 var r = s.getRangeAt(0);
26047 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26052 this.execCmd('useCSS', true);
26053 this.execCmd('styleWithCSS', false);
26056 this.owner.fireEvent('activate', this);
26060 adjustFont: function(btn){
26061 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26062 //if(Roo.isSafari){ // safari
26065 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26066 if(Roo.isSafari){ // safari
26067 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26068 v = (v < 10) ? 10 : v;
26069 v = (v > 48) ? 48 : v;
26070 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26075 v = Math.max(1, v+adjust);
26077 this.execCmd('FontSize', v );
26080 onEditorEvent : function(e)
26082 this.owner.fireEvent('editorevent', this, e);
26083 // this.updateToolbar();
26084 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26087 insertTag : function(tg)
26089 // could be a bit smarter... -> wrap the current selected tRoo..
26090 if (tg.toLowerCase() == 'span' ||
26091 tg.toLowerCase() == 'code' ||
26092 tg.toLowerCase() == 'sup' ||
26093 tg.toLowerCase() == 'sub'
26096 range = this.createRange(this.getSelection());
26097 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26098 wrappingNode.appendChild(range.extractContents());
26099 range.insertNode(wrappingNode);
26106 this.execCmd("formatblock", tg);
26110 insertText : function(txt)
26114 var range = this.createRange();
26115 range.deleteContents();
26116 //alert(Sender.getAttribute('label'));
26118 range.insertNode(this.doc.createTextNode(txt));
26124 * Executes a Midas editor command on the editor document and performs necessary focus and
26125 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26126 * @param {String} cmd The Midas command
26127 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26129 relayCmd : function(cmd, value){
26131 this.execCmd(cmd, value);
26132 this.owner.fireEvent('editorevent', this);
26133 //this.updateToolbar();
26134 this.owner.deferFocus();
26138 * Executes a Midas editor command directly on the editor document.
26139 * For visual commands, you should use {@link #relayCmd} instead.
26140 * <b>This should only be called after the editor is initialized.</b>
26141 * @param {String} cmd The Midas command
26142 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26144 execCmd : function(cmd, value){
26145 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26152 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26154 * @param {String} text | dom node..
26156 insertAtCursor : function(text)
26159 if(!this.activated){
26165 var r = this.doc.selection.createRange();
26176 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26180 // from jquery ui (MIT licenced)
26182 var win = this.win;
26184 if (win.getSelection && win.getSelection().getRangeAt) {
26185 range = win.getSelection().getRangeAt(0);
26186 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26187 range.insertNode(node);
26188 } else if (win.document.selection && win.document.selection.createRange) {
26189 // no firefox support
26190 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26191 win.document.selection.createRange().pasteHTML(txt);
26193 // no firefox support
26194 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26195 this.execCmd('InsertHTML', txt);
26204 mozKeyPress : function(e){
26206 var c = e.getCharCode(), cmd;
26209 c = String.fromCharCode(c).toLowerCase();
26223 this.cleanUpPaste.defer(100, this);
26231 e.preventDefault();
26239 fixKeys : function(){ // load time branching for fastest keydown performance
26241 return function(e){
26242 var k = e.getKey(), r;
26245 r = this.doc.selection.createRange();
26248 r.pasteHTML('    ');
26255 r = this.doc.selection.createRange();
26257 var target = r.parentElement();
26258 if(!target || target.tagName.toLowerCase() != 'li'){
26260 r.pasteHTML('<br />');
26266 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26267 this.cleanUpPaste.defer(100, this);
26273 }else if(Roo.isOpera){
26274 return function(e){
26275 var k = e.getKey();
26279 this.execCmd('InsertHTML','    ');
26282 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26283 this.cleanUpPaste.defer(100, this);
26288 }else if(Roo.isSafari){
26289 return function(e){
26290 var k = e.getKey();
26294 this.execCmd('InsertText','\t');
26298 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26299 this.cleanUpPaste.defer(100, this);
26307 getAllAncestors: function()
26309 var p = this.getSelectedNode();
26312 a.push(p); // push blank onto stack..
26313 p = this.getParentElement();
26317 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26321 a.push(this.doc.body);
26325 lastSelNode : false,
26328 getSelection : function()
26330 this.assignDocWin();
26331 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26334 getSelectedNode: function()
26336 // this may only work on Gecko!!!
26338 // should we cache this!!!!
26343 var range = this.createRange(this.getSelection()).cloneRange();
26346 var parent = range.parentElement();
26348 var testRange = range.duplicate();
26349 testRange.moveToElementText(parent);
26350 if (testRange.inRange(range)) {
26353 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26356 parent = parent.parentElement;
26361 // is ancestor a text element.
26362 var ac = range.commonAncestorContainer;
26363 if (ac.nodeType == 3) {
26364 ac = ac.parentNode;
26367 var ar = ac.childNodes;
26370 var other_nodes = [];
26371 var has_other_nodes = false;
26372 for (var i=0;i<ar.length;i++) {
26373 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26376 // fullly contained node.
26378 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26383 // probably selected..
26384 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26385 other_nodes.push(ar[i]);
26389 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26394 has_other_nodes = true;
26396 if (!nodes.length && other_nodes.length) {
26397 nodes= other_nodes;
26399 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26405 createRange: function(sel)
26407 // this has strange effects when using with
26408 // top toolbar - not sure if it's a great idea.
26409 //this.editor.contentWindow.focus();
26410 if (typeof sel != "undefined") {
26412 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26414 return this.doc.createRange();
26417 return this.doc.createRange();
26420 getParentElement: function()
26423 this.assignDocWin();
26424 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26426 var range = this.createRange(sel);
26429 var p = range.commonAncestorContainer;
26430 while (p.nodeType == 3) { // text node
26441 * Range intersection.. the hard stuff...
26445 * [ -- selected range --- ]
26449 * if end is before start or hits it. fail.
26450 * if start is after end or hits it fail.
26452 * if either hits (but other is outside. - then it's not
26458 // @see http://www.thismuchiknow.co.uk/?p=64.
26459 rangeIntersectsNode : function(range, node)
26461 var nodeRange = node.ownerDocument.createRange();
26463 nodeRange.selectNode(node);
26465 nodeRange.selectNodeContents(node);
26468 var rangeStartRange = range.cloneRange();
26469 rangeStartRange.collapse(true);
26471 var rangeEndRange = range.cloneRange();
26472 rangeEndRange.collapse(false);
26474 var nodeStartRange = nodeRange.cloneRange();
26475 nodeStartRange.collapse(true);
26477 var nodeEndRange = nodeRange.cloneRange();
26478 nodeEndRange.collapse(false);
26480 return rangeStartRange.compareBoundaryPoints(
26481 Range.START_TO_START, nodeEndRange) == -1 &&
26482 rangeEndRange.compareBoundaryPoints(
26483 Range.START_TO_START, nodeStartRange) == 1;
26487 rangeCompareNode : function(range, node)
26489 var nodeRange = node.ownerDocument.createRange();
26491 nodeRange.selectNode(node);
26493 nodeRange.selectNodeContents(node);
26497 range.collapse(true);
26499 nodeRange.collapse(true);
26501 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26502 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26504 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26506 var nodeIsBefore = ss == 1;
26507 var nodeIsAfter = ee == -1;
26509 if (nodeIsBefore && nodeIsAfter) {
26512 if (!nodeIsBefore && nodeIsAfter) {
26513 return 1; //right trailed.
26516 if (nodeIsBefore && !nodeIsAfter) {
26517 return 2; // left trailed.
26523 // private? - in a new class?
26524 cleanUpPaste : function()
26526 // cleans up the whole document..
26527 Roo.log('cleanuppaste');
26529 this.cleanUpChildren(this.doc.body);
26530 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26531 if (clean != this.doc.body.innerHTML) {
26532 this.doc.body.innerHTML = clean;
26537 cleanWordChars : function(input) {// change the chars to hex code
26538 var he = Roo.HtmlEditorCore;
26540 var output = input;
26541 Roo.each(he.swapCodes, function(sw) {
26542 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26544 output = output.replace(swapper, sw[1]);
26551 cleanUpChildren : function (n)
26553 if (!n.childNodes.length) {
26556 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26557 this.cleanUpChild(n.childNodes[i]);
26564 cleanUpChild : function (node)
26567 //console.log(node);
26568 if (node.nodeName == "#text") {
26569 // clean up silly Windows -- stuff?
26572 if (node.nodeName == "#comment") {
26573 if (!this.allowComments) {
26574 node.parentNode.removeChild(node);
26576 // clean up silly Windows -- stuff?
26579 var lcname = node.tagName.toLowerCase();
26580 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26581 // whitelist of tags..
26583 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26585 node.parentNode.removeChild(node);
26590 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26592 // spans with no attributes - just remove them..
26593 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26594 remove_keep_children = true;
26597 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26598 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26600 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26601 // remove_keep_children = true;
26604 if (remove_keep_children) {
26605 this.cleanUpChildren(node);
26606 // inserts everything just before this node...
26607 while (node.childNodes.length) {
26608 var cn = node.childNodes[0];
26609 node.removeChild(cn);
26610 node.parentNode.insertBefore(cn, node);
26612 node.parentNode.removeChild(node);
26616 if (!node.attributes || !node.attributes.length) {
26621 this.cleanUpChildren(node);
26625 function cleanAttr(n,v)
26628 if (v.match(/^\./) || v.match(/^\//)) {
26631 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26634 if (v.match(/^#/)) {
26637 if (v.match(/^\{/)) { // allow template editing.
26640 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26641 node.removeAttribute(n);
26645 var cwhite = this.cwhite;
26646 var cblack = this.cblack;
26648 function cleanStyle(n,v)
26650 if (v.match(/expression/)) { //XSS?? should we even bother..
26651 node.removeAttribute(n);
26655 var parts = v.split(/;/);
26658 Roo.each(parts, function(p) {
26659 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26663 var l = p.split(':').shift().replace(/\s+/g,'');
26664 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26666 if ( cwhite.length && cblack.indexOf(l) > -1) {
26667 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26668 //node.removeAttribute(n);
26672 // only allow 'c whitelisted system attributes'
26673 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26674 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26675 //node.removeAttribute(n);
26685 if (clean.length) {
26686 node.setAttribute(n, clean.join(';'));
26688 node.removeAttribute(n);
26694 for (var i = node.attributes.length-1; i > -1 ; i--) {
26695 var a = node.attributes[i];
26698 if (a.name.toLowerCase().substr(0,2)=='on') {
26699 node.removeAttribute(a.name);
26702 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26703 node.removeAttribute(a.name);
26706 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26707 cleanAttr(a.name,a.value); // fixme..
26710 if (a.name == 'style') {
26711 cleanStyle(a.name,a.value);
26714 /// clean up MS crap..
26715 // tecnically this should be a list of valid class'es..
26718 if (a.name == 'class') {
26719 if (a.value.match(/^Mso/)) {
26720 node.removeAttribute('class');
26723 if (a.value.match(/^body$/)) {
26724 node.removeAttribute('class');
26735 this.cleanUpChildren(node);
26741 * Clean up MS wordisms...
26743 cleanWord : function(node)
26746 this.cleanWord(this.doc.body);
26751 node.nodeName == 'SPAN' &&
26752 !node.hasAttributes() &&
26753 node.childNodes.length == 1 &&
26754 node.firstChild.nodeName == "#text"
26756 var textNode = node.firstChild;
26757 node.removeChild(textNode);
26758 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26759 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26761 node.parentNode.insertBefore(textNode, node);
26762 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26763 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26765 node.parentNode.removeChild(node);
26768 if (node.nodeName == "#text") {
26769 // clean up silly Windows -- stuff?
26772 if (node.nodeName == "#comment") {
26773 node.parentNode.removeChild(node);
26774 // clean up silly Windows -- stuff?
26778 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26779 node.parentNode.removeChild(node);
26782 //Roo.log(node.tagName);
26783 // remove - but keep children..
26784 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26785 //Roo.log('-- removed');
26786 while (node.childNodes.length) {
26787 var cn = node.childNodes[0];
26788 node.removeChild(cn);
26789 node.parentNode.insertBefore(cn, node);
26790 // move node to parent - and clean it..
26791 this.cleanWord(cn);
26793 node.parentNode.removeChild(node);
26794 /// no need to iterate chidlren = it's got none..
26795 //this.iterateChildren(node, this.cleanWord);
26799 if (node.className.length) {
26801 var cn = node.className.split(/\W+/);
26803 Roo.each(cn, function(cls) {
26804 if (cls.match(/Mso[a-zA-Z]+/)) {
26809 node.className = cna.length ? cna.join(' ') : '';
26811 node.removeAttribute("class");
26815 if (node.hasAttribute("lang")) {
26816 node.removeAttribute("lang");
26819 if (node.hasAttribute("style")) {
26821 var styles = node.getAttribute("style").split(";");
26823 Roo.each(styles, function(s) {
26824 if (!s.match(/:/)) {
26827 var kv = s.split(":");
26828 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26831 // what ever is left... we allow.
26834 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26835 if (!nstyle.length) {
26836 node.removeAttribute('style');
26839 this.iterateChildren(node, this.cleanWord);
26845 * iterateChildren of a Node, calling fn each time, using this as the scole..
26846 * @param {DomNode} node node to iterate children of.
26847 * @param {Function} fn method of this class to call on each item.
26849 iterateChildren : function(node, fn)
26851 if (!node.childNodes.length) {
26854 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26855 fn.call(this, node.childNodes[i])
26861 * cleanTableWidths.
26863 * Quite often pasting from word etc.. results in tables with column and widths.
26864 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26867 cleanTableWidths : function(node)
26872 this.cleanTableWidths(this.doc.body);
26877 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26880 Roo.log(node.tagName);
26881 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26882 this.iterateChildren(node, this.cleanTableWidths);
26885 if (node.hasAttribute('width')) {
26886 node.removeAttribute('width');
26890 if (node.hasAttribute("style")) {
26893 var styles = node.getAttribute("style").split(";");
26895 Roo.each(styles, function(s) {
26896 if (!s.match(/:/)) {
26899 var kv = s.split(":");
26900 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26903 // what ever is left... we allow.
26906 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26907 if (!nstyle.length) {
26908 node.removeAttribute('style');
26912 this.iterateChildren(node, this.cleanTableWidths);
26920 domToHTML : function(currentElement, depth, nopadtext) {
26922 depth = depth || 0;
26923 nopadtext = nopadtext || false;
26925 if (!currentElement) {
26926 return this.domToHTML(this.doc.body);
26929 //Roo.log(currentElement);
26931 var allText = false;
26932 var nodeName = currentElement.nodeName;
26933 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26935 if (nodeName == '#text') {
26937 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26942 if (nodeName != 'BODY') {
26945 // Prints the node tagName, such as <A>, <IMG>, etc
26948 for(i = 0; i < currentElement.attributes.length;i++) {
26950 var aname = currentElement.attributes.item(i).name;
26951 if (!currentElement.attributes.item(i).value.length) {
26954 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26957 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26966 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26969 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26974 // Traverse the tree
26976 var currentElementChild = currentElement.childNodes.item(i);
26977 var allText = true;
26978 var innerHTML = '';
26980 while (currentElementChild) {
26981 // Formatting code (indent the tree so it looks nice on the screen)
26982 var nopad = nopadtext;
26983 if (lastnode == 'SPAN') {
26987 if (currentElementChild.nodeName == '#text') {
26988 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26989 toadd = nopadtext ? toadd : toadd.trim();
26990 if (!nopad && toadd.length > 80) {
26991 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26993 innerHTML += toadd;
26996 currentElementChild = currentElement.childNodes.item(i);
27002 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
27004 // Recursively traverse the tree structure of the child node
27005 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
27006 lastnode = currentElementChild.nodeName;
27008 currentElementChild=currentElement.childNodes.item(i);
27014 // The remaining code is mostly for formatting the tree
27015 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
27020 ret+= "</"+tagName+">";
27026 applyBlacklists : function()
27028 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
27029 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
27033 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27034 if (b.indexOf(tag) > -1) {
27037 this.white.push(tag);
27041 Roo.each(w, function(tag) {
27042 if (b.indexOf(tag) > -1) {
27045 if (this.white.indexOf(tag) > -1) {
27048 this.white.push(tag);
27053 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27054 if (w.indexOf(tag) > -1) {
27057 this.black.push(tag);
27061 Roo.each(b, function(tag) {
27062 if (w.indexOf(tag) > -1) {
27065 if (this.black.indexOf(tag) > -1) {
27068 this.black.push(tag);
27073 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27074 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27078 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27079 if (b.indexOf(tag) > -1) {
27082 this.cwhite.push(tag);
27086 Roo.each(w, function(tag) {
27087 if (b.indexOf(tag) > -1) {
27090 if (this.cwhite.indexOf(tag) > -1) {
27093 this.cwhite.push(tag);
27098 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27099 if (w.indexOf(tag) > -1) {
27102 this.cblack.push(tag);
27106 Roo.each(b, function(tag) {
27107 if (w.indexOf(tag) > -1) {
27110 if (this.cblack.indexOf(tag) > -1) {
27113 this.cblack.push(tag);
27118 setStylesheets : function(stylesheets)
27120 if(typeof(stylesheets) == 'string'){
27121 Roo.get(this.iframe.contentDocument.head).createChild({
27123 rel : 'stylesheet',
27132 Roo.each(stylesheets, function(s) {
27137 Roo.get(_this.iframe.contentDocument.head).createChild({
27139 rel : 'stylesheet',
27148 removeStylesheets : function()
27152 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27157 setStyle : function(style)
27159 Roo.get(this.iframe.contentDocument.head).createChild({
27168 // hide stuff that is not compatible
27182 * @event specialkey
27186 * @cfg {String} fieldClass @hide
27189 * @cfg {String} focusClass @hide
27192 * @cfg {String} autoCreate @hide
27195 * @cfg {String} inputType @hide
27198 * @cfg {String} invalidClass @hide
27201 * @cfg {String} invalidText @hide
27204 * @cfg {String} msgFx @hide
27207 * @cfg {String} validateOnBlur @hide
27211 Roo.HtmlEditorCore.white = [
27212 'area', 'br', 'img', 'input', 'hr', 'wbr',
27214 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27215 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27216 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27217 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27218 'table', 'ul', 'xmp',
27220 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27223 'dir', 'menu', 'ol', 'ul', 'dl',
27229 Roo.HtmlEditorCore.black = [
27230 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27232 'base', 'basefont', 'bgsound', 'blink', 'body',
27233 'frame', 'frameset', 'head', 'html', 'ilayer',
27234 'iframe', 'layer', 'link', 'meta', 'object',
27235 'script', 'style' ,'title', 'xml' // clean later..
27237 Roo.HtmlEditorCore.clean = [
27238 'script', 'style', 'title', 'xml'
27240 Roo.HtmlEditorCore.remove = [
27245 Roo.HtmlEditorCore.ablack = [
27249 Roo.HtmlEditorCore.aclean = [
27250 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27254 Roo.HtmlEditorCore.pwhite= [
27255 'http', 'https', 'mailto'
27258 // white listed style attributes.
27259 Roo.HtmlEditorCore.cwhite= [
27260 // 'text-align', /// default is to allow most things..
27266 // black listed style attributes.
27267 Roo.HtmlEditorCore.cblack= [
27268 // 'font-size' -- this can be set by the project
27272 Roo.HtmlEditorCore.swapCodes =[
27273 [ 8211, "–" ],
27274 [ 8212, "—" ],
27291 * @class Roo.bootstrap.HtmlEditor
27292 * @extends Roo.bootstrap.TextArea
27293 * Bootstrap HtmlEditor class
27296 * Create a new HtmlEditor
27297 * @param {Object} config The config object
27300 Roo.bootstrap.HtmlEditor = function(config){
27301 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27302 if (!this.toolbars) {
27303 this.toolbars = [];
27306 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27309 * @event initialize
27310 * Fires when the editor is fully initialized (including the iframe)
27311 * @param {HtmlEditor} this
27316 * Fires when the editor is first receives the focus. Any insertion must wait
27317 * until after this event.
27318 * @param {HtmlEditor} this
27322 * @event beforesync
27323 * Fires before the textarea is updated with content from the editor iframe. Return false
27324 * to cancel the sync.
27325 * @param {HtmlEditor} this
27326 * @param {String} html
27330 * @event beforepush
27331 * Fires before the iframe editor is updated with content from the textarea. Return false
27332 * to cancel the push.
27333 * @param {HtmlEditor} this
27334 * @param {String} html
27339 * Fires when the textarea is updated with content from the editor iframe.
27340 * @param {HtmlEditor} this
27341 * @param {String} html
27346 * Fires when the iframe editor is updated with content from the textarea.
27347 * @param {HtmlEditor} this
27348 * @param {String} html
27352 * @event editmodechange
27353 * Fires when the editor switches edit modes
27354 * @param {HtmlEditor} this
27355 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27357 editmodechange: true,
27359 * @event editorevent
27360 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27361 * @param {HtmlEditor} this
27365 * @event firstfocus
27366 * Fires when on first focus - needed by toolbars..
27367 * @param {HtmlEditor} this
27372 * Auto save the htmlEditor value as a file into Events
27373 * @param {HtmlEditor} this
27377 * @event savedpreview
27378 * preview the saved version of htmlEditor
27379 * @param {HtmlEditor} this
27386 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27390 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27395 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27400 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27405 * @cfg {Number} height (in pixels)
27409 * @cfg {Number} width (in pixels)
27414 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27417 stylesheets: false,
27422 // private properties
27423 validationEvent : false,
27425 initialized : false,
27428 onFocus : Roo.emptyFn,
27430 hideMode:'offsets',
27432 tbContainer : false,
27436 toolbarContainer :function() {
27437 return this.wrap.select('.x-html-editor-tb',true).first();
27441 * Protected method that will not generally be called directly. It
27442 * is called when the editor creates its toolbar. Override this method if you need to
27443 * add custom toolbar buttons.
27444 * @param {HtmlEditor} editor
27446 createToolbar : function(){
27447 Roo.log('renewing');
27448 Roo.log("create toolbars");
27450 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27451 this.toolbars[0].render(this.toolbarContainer());
27455 // if (!editor.toolbars || !editor.toolbars.length) {
27456 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27459 // for (var i =0 ; i < editor.toolbars.length;i++) {
27460 // editor.toolbars[i] = Roo.factory(
27461 // typeof(editor.toolbars[i]) == 'string' ?
27462 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27463 // Roo.bootstrap.HtmlEditor);
27464 // editor.toolbars[i].init(editor);
27470 onRender : function(ct, position)
27472 // Roo.log("Call onRender: " + this.xtype);
27474 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27476 this.wrap = this.inputEl().wrap({
27477 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27480 this.editorcore.onRender(ct, position);
27482 if (this.resizable) {
27483 this.resizeEl = new Roo.Resizable(this.wrap, {
27487 minHeight : this.height,
27488 height: this.height,
27489 handles : this.resizable,
27492 resize : function(r, w, h) {
27493 _t.onResize(w,h); // -something
27499 this.createToolbar(this);
27502 if(!this.width && this.resizable){
27503 this.setSize(this.wrap.getSize());
27505 if (this.resizeEl) {
27506 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27507 // should trigger onReize..
27513 onResize : function(w, h)
27515 Roo.log('resize: ' +w + ',' + h );
27516 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27520 if(this.inputEl() ){
27521 if(typeof w == 'number'){
27522 var aw = w - this.wrap.getFrameWidth('lr');
27523 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27526 if(typeof h == 'number'){
27527 var tbh = -11; // fixme it needs to tool bar size!
27528 for (var i =0; i < this.toolbars.length;i++) {
27529 // fixme - ask toolbars for heights?
27530 tbh += this.toolbars[i].el.getHeight();
27531 //if (this.toolbars[i].footer) {
27532 // tbh += this.toolbars[i].footer.el.getHeight();
27540 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27541 ah -= 5; // knock a few pixes off for look..
27542 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27546 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27547 this.editorcore.onResize(ew,eh);
27552 * Toggles the editor between standard and source edit mode.
27553 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27555 toggleSourceEdit : function(sourceEditMode)
27557 this.editorcore.toggleSourceEdit(sourceEditMode);
27559 if(this.editorcore.sourceEditMode){
27560 Roo.log('editor - showing textarea');
27563 // Roo.log(this.syncValue());
27565 this.inputEl().removeClass(['hide', 'x-hidden']);
27566 this.inputEl().dom.removeAttribute('tabIndex');
27567 this.inputEl().focus();
27569 Roo.log('editor - hiding textarea');
27571 // Roo.log(this.pushValue());
27574 this.inputEl().addClass(['hide', 'x-hidden']);
27575 this.inputEl().dom.setAttribute('tabIndex', -1);
27576 //this.deferFocus();
27579 if(this.resizable){
27580 this.setSize(this.wrap.getSize());
27583 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27586 // private (for BoxComponent)
27587 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27589 // private (for BoxComponent)
27590 getResizeEl : function(){
27594 // private (for BoxComponent)
27595 getPositionEl : function(){
27600 initEvents : function(){
27601 this.originalValue = this.getValue();
27605 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27608 // markInvalid : Roo.emptyFn,
27610 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27613 // clearInvalid : Roo.emptyFn,
27615 setValue : function(v){
27616 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27617 this.editorcore.pushValue();
27622 deferFocus : function(){
27623 this.focus.defer(10, this);
27627 focus : function(){
27628 this.editorcore.focus();
27634 onDestroy : function(){
27640 for (var i =0; i < this.toolbars.length;i++) {
27641 // fixme - ask toolbars for heights?
27642 this.toolbars[i].onDestroy();
27645 this.wrap.dom.innerHTML = '';
27646 this.wrap.remove();
27651 onFirstFocus : function(){
27652 //Roo.log("onFirstFocus");
27653 this.editorcore.onFirstFocus();
27654 for (var i =0; i < this.toolbars.length;i++) {
27655 this.toolbars[i].onFirstFocus();
27661 syncValue : function()
27663 this.editorcore.syncValue();
27666 pushValue : function()
27668 this.editorcore.pushValue();
27672 // hide stuff that is not compatible
27686 * @event specialkey
27690 * @cfg {String} fieldClass @hide
27693 * @cfg {String} focusClass @hide
27696 * @cfg {String} autoCreate @hide
27699 * @cfg {String} inputType @hide
27703 * @cfg {String} invalidText @hide
27706 * @cfg {String} msgFx @hide
27709 * @cfg {String} validateOnBlur @hide
27718 Roo.namespace('Roo.bootstrap.htmleditor');
27720 * @class Roo.bootstrap.HtmlEditorToolbar1
27726 new Roo.bootstrap.HtmlEditor({
27729 new Roo.bootstrap.HtmlEditorToolbar1({
27730 disable : { fonts: 1 , format: 1, ..., ... , ...],
27736 * @cfg {Object} disable List of elements to disable..
27737 * @cfg {Array} btns List of additional buttons.
27741 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27744 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27747 Roo.apply(this, config);
27749 // default disabled, based on 'good practice'..
27750 this.disable = this.disable || {};
27751 Roo.applyIf(this.disable, {
27754 specialElements : true
27756 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27758 this.editor = config.editor;
27759 this.editorcore = config.editor.editorcore;
27761 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27763 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27764 // dont call parent... till later.
27766 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27771 editorcore : false,
27776 "h1","h2","h3","h4","h5","h6",
27778 "abbr", "acronym", "address", "cite", "samp", "var",
27782 onRender : function(ct, position)
27784 // Roo.log("Call onRender: " + this.xtype);
27786 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27788 this.el.dom.style.marginBottom = '0';
27790 var editorcore = this.editorcore;
27791 var editor= this.editor;
27794 var btn = function(id,cmd , toggle, handler, html){
27796 var event = toggle ? 'toggle' : 'click';
27801 xns: Roo.bootstrap,
27805 enableToggle:toggle !== false,
27807 pressed : toggle ? false : null,
27810 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27811 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27817 // var cb_box = function...
27822 xns: Roo.bootstrap,
27827 xns: Roo.bootstrap,
27831 Roo.each(this.formats, function(f) {
27832 style.menu.items.push({
27834 xns: Roo.bootstrap,
27835 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27840 editorcore.insertTag(this.tagname);
27847 children.push(style);
27849 btn('bold',false,true);
27850 btn('italic',false,true);
27851 btn('align-left', 'justifyleft',true);
27852 btn('align-center', 'justifycenter',true);
27853 btn('align-right' , 'justifyright',true);
27854 btn('link', false, false, function(btn) {
27855 //Roo.log("create link?");
27856 var url = prompt(this.createLinkText, this.defaultLinkValue);
27857 if(url && url != 'http:/'+'/'){
27858 this.editorcore.relayCmd('createlink', url);
27861 btn('list','insertunorderedlist',true);
27862 btn('pencil', false,true, function(btn){
27864 this.toggleSourceEdit(btn.pressed);
27867 if (this.editor.btns.length > 0) {
27868 for (var i = 0; i<this.editor.btns.length; i++) {
27869 children.push(this.editor.btns[i]);
27877 xns: Roo.bootstrap,
27882 xns: Roo.bootstrap,
27887 cog.menu.items.push({
27889 xns: Roo.bootstrap,
27890 html : Clean styles,
27895 editorcore.insertTag(this.tagname);
27904 this.xtype = 'NavSimplebar';
27906 for(var i=0;i< children.length;i++) {
27908 this.buttons.add(this.addxtypeChild(children[i]));
27912 editor.on('editorevent', this.updateToolbar, this);
27914 onBtnClick : function(id)
27916 this.editorcore.relayCmd(id);
27917 this.editorcore.focus();
27921 * Protected method that will not generally be called directly. It triggers
27922 * a toolbar update by reading the markup state of the current selection in the editor.
27924 updateToolbar: function(){
27926 if(!this.editorcore.activated){
27927 this.editor.onFirstFocus(); // is this neeed?
27931 var btns = this.buttons;
27932 var doc = this.editorcore.doc;
27933 btns.get('bold').setActive(doc.queryCommandState('bold'));
27934 btns.get('italic').setActive(doc.queryCommandState('italic'));
27935 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27937 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27938 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27939 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27941 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27942 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27945 var ans = this.editorcore.getAllAncestors();
27946 if (this.formatCombo) {
27949 var store = this.formatCombo.store;
27950 this.formatCombo.setValue("");
27951 for (var i =0; i < ans.length;i++) {
27952 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27954 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27962 // hides menus... - so this cant be on a menu...
27963 Roo.bootstrap.MenuMgr.hideAll();
27965 Roo.bootstrap.MenuMgr.hideAll();
27966 //this.editorsyncValue();
27968 onFirstFocus: function() {
27969 this.buttons.each(function(item){
27973 toggleSourceEdit : function(sourceEditMode){
27976 if(sourceEditMode){
27977 Roo.log("disabling buttons");
27978 this.buttons.each( function(item){
27979 if(item.cmd != 'pencil'){
27985 Roo.log("enabling buttons");
27986 if(this.editorcore.initialized){
27987 this.buttons.each( function(item){
27993 Roo.log("calling toggole on editor");
27994 // tell the editor that it's been pressed..
27995 this.editor.toggleSourceEdit(sourceEditMode);
28009 * @class Roo.bootstrap.Markdown
28010 * @extends Roo.bootstrap.TextArea
28011 * Bootstrap Showdown editable area
28012 * @cfg {string} content
28015 * Create a new Showdown
28018 Roo.bootstrap.Markdown = function(config){
28019 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
28023 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
28027 initEvents : function()
28030 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
28031 this.markdownEl = this.el.createChild({
28032 cls : 'roo-markdown-area'
28034 this.inputEl().addClass('d-none');
28035 if (this.getValue() == '') {
28036 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28039 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28041 this.markdownEl.on('click', this.toggleTextEdit, this);
28042 this.on('blur', this.toggleTextEdit, this);
28043 this.on('specialkey', this.resizeTextArea, this);
28046 toggleTextEdit : function()
28048 var sh = this.markdownEl.getHeight();
28049 this.inputEl().addClass('d-none');
28050 this.markdownEl.addClass('d-none');
28051 if (!this.editing) {
28053 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28054 this.inputEl().removeClass('d-none');
28055 this.inputEl().focus();
28056 this.editing = true;
28059 // show showdown...
28060 this.updateMarkdown();
28061 this.markdownEl.removeClass('d-none');
28062 this.editing = false;
28065 updateMarkdown : function()
28067 if (this.getValue() == '') {
28068 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28072 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28075 resizeTextArea: function () {
28078 Roo.log([sh, this.getValue().split("\n").length * 30]);
28079 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28081 setValue : function(val)
28083 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28084 if (!this.editing) {
28085 this.updateMarkdown();
28091 if (!this.editing) {
28092 this.toggleTextEdit();
28100 * Ext JS Library 1.1.1
28101 * Copyright(c) 2006-2007, Ext JS, LLC.
28103 * Originally Released Under LGPL - original licence link has changed is not relivant.
28106 * <script type="text/javascript">
28110 * @class Roo.bootstrap.PagingToolbar
28111 * @extends Roo.bootstrap.NavSimplebar
28112 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28114 * Create a new PagingToolbar
28115 * @param {Object} config The config object
28116 * @param {Roo.data.Store} store
28118 Roo.bootstrap.PagingToolbar = function(config)
28120 // old args format still supported... - xtype is prefered..
28121 // created from xtype...
28123 this.ds = config.dataSource;
28125 if (config.store && !this.ds) {
28126 this.store= Roo.factory(config.store, Roo.data);
28127 this.ds = this.store;
28128 this.ds.xmodule = this.xmodule || false;
28131 this.toolbarItems = [];
28132 if (config.items) {
28133 this.toolbarItems = config.items;
28136 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28141 this.bind(this.ds);
28144 if (Roo.bootstrap.version == 4) {
28145 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28147 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28152 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28154 * @cfg {Roo.bootstrap.Button} buttons[]
28155 * Buttons for the toolbar
28158 * @cfg {Roo.data.Store} store
28159 * The underlying data store providing the paged data
28162 * @cfg {String/HTMLElement/Element} container
28163 * container The id or element that will contain the toolbar
28166 * @cfg {Boolean} displayInfo
28167 * True to display the displayMsg (defaults to false)
28170 * @cfg {Number} pageSize
28171 * The number of records to display per page (defaults to 20)
28175 * @cfg {String} displayMsg
28176 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28178 displayMsg : 'Displaying {0} - {1} of {2}',
28180 * @cfg {String} emptyMsg
28181 * The message to display when no records are found (defaults to "No data to display")
28183 emptyMsg : 'No data to display',
28185 * Customizable piece of the default paging text (defaults to "Page")
28188 beforePageText : "Page",
28190 * Customizable piece of the default paging text (defaults to "of %0")
28193 afterPageText : "of {0}",
28195 * Customizable piece of the default paging text (defaults to "First Page")
28198 firstText : "First Page",
28200 * Customizable piece of the default paging text (defaults to "Previous Page")
28203 prevText : "Previous Page",
28205 * Customizable piece of the default paging text (defaults to "Next Page")
28208 nextText : "Next Page",
28210 * Customizable piece of the default paging text (defaults to "Last Page")
28213 lastText : "Last Page",
28215 * Customizable piece of the default paging text (defaults to "Refresh")
28218 refreshText : "Refresh",
28222 onRender : function(ct, position)
28224 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28225 this.navgroup.parentId = this.id;
28226 this.navgroup.onRender(this.el, null);
28227 // add the buttons to the navgroup
28229 if(this.displayInfo){
28230 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28231 this.displayEl = this.el.select('.x-paging-info', true).first();
28232 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28233 // this.displayEl = navel.el.select('span',true).first();
28239 Roo.each(_this.buttons, function(e){ // this might need to use render????
28240 Roo.factory(e).render(_this.el);
28244 Roo.each(_this.toolbarItems, function(e) {
28245 _this.navgroup.addItem(e);
28249 this.first = this.navgroup.addItem({
28250 tooltip: this.firstText,
28251 cls: "prev btn-outline-secondary",
28252 html : ' <i class="fa fa-step-backward"></i>',
28254 preventDefault: true,
28255 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28258 this.prev = this.navgroup.addItem({
28259 tooltip: this.prevText,
28260 cls: "prev btn-outline-secondary",
28261 html : ' <i class="fa fa-backward"></i>',
28263 preventDefault: true,
28264 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28266 //this.addSeparator();
28269 var field = this.navgroup.addItem( {
28271 cls : 'x-paging-position btn-outline-secondary',
28273 html : this.beforePageText +
28274 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28275 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28278 this.field = field.el.select('input', true).first();
28279 this.field.on("keydown", this.onPagingKeydown, this);
28280 this.field.on("focus", function(){this.dom.select();});
28283 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28284 //this.field.setHeight(18);
28285 //this.addSeparator();
28286 this.next = this.navgroup.addItem({
28287 tooltip: this.nextText,
28288 cls: "next btn-outline-secondary",
28289 html : ' <i class="fa fa-forward"></i>',
28291 preventDefault: true,
28292 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28294 this.last = this.navgroup.addItem({
28295 tooltip: this.lastText,
28296 html : ' <i class="fa fa-step-forward"></i>',
28297 cls: "next btn-outline-secondary",
28299 preventDefault: true,
28300 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28302 //this.addSeparator();
28303 this.loading = this.navgroup.addItem({
28304 tooltip: this.refreshText,
28305 cls: "btn-outline-secondary",
28306 html : ' <i class="fa fa-refresh"></i>',
28307 preventDefault: true,
28308 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28314 updateInfo : function(){
28315 if(this.displayEl){
28316 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28317 var msg = count == 0 ?
28321 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28323 this.displayEl.update(msg);
28328 onLoad : function(ds, r, o)
28330 this.cursor = o.params && o.params.start ? o.params.start : 0;
28332 var d = this.getPageData(),
28337 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28338 this.field.dom.value = ap;
28339 this.first.setDisabled(ap == 1);
28340 this.prev.setDisabled(ap == 1);
28341 this.next.setDisabled(ap == ps);
28342 this.last.setDisabled(ap == ps);
28343 this.loading.enable();
28348 getPageData : function(){
28349 var total = this.ds.getTotalCount();
28352 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28353 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28358 onLoadError : function(){
28359 this.loading.enable();
28363 onPagingKeydown : function(e){
28364 var k = e.getKey();
28365 var d = this.getPageData();
28367 var v = this.field.dom.value, pageNum;
28368 if(!v || isNaN(pageNum = parseInt(v, 10))){
28369 this.field.dom.value = d.activePage;
28372 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28373 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28376 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))
28378 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28379 this.field.dom.value = pageNum;
28380 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28383 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28385 var v = this.field.dom.value, pageNum;
28386 var increment = (e.shiftKey) ? 10 : 1;
28387 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28390 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28391 this.field.dom.value = d.activePage;
28394 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28396 this.field.dom.value = parseInt(v, 10) + increment;
28397 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28398 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28405 beforeLoad : function(){
28407 this.loading.disable();
28412 onClick : function(which){
28421 ds.load({params:{start: 0, limit: this.pageSize}});
28424 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28427 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28430 var total = ds.getTotalCount();
28431 var extra = total % this.pageSize;
28432 var lastStart = extra ? (total - extra) : total-this.pageSize;
28433 ds.load({params:{start: lastStart, limit: this.pageSize}});
28436 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28442 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28443 * @param {Roo.data.Store} store The data store to unbind
28445 unbind : function(ds){
28446 ds.un("beforeload", this.beforeLoad, this);
28447 ds.un("load", this.onLoad, this);
28448 ds.un("loadexception", this.onLoadError, this);
28449 ds.un("remove", this.updateInfo, this);
28450 ds.un("add", this.updateInfo, this);
28451 this.ds = undefined;
28455 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28456 * @param {Roo.data.Store} store The data store to bind
28458 bind : function(ds){
28459 ds.on("beforeload", this.beforeLoad, this);
28460 ds.on("load", this.onLoad, this);
28461 ds.on("loadexception", this.onLoadError, this);
28462 ds.on("remove", this.updateInfo, this);
28463 ds.on("add", this.updateInfo, this);
28474 * @class Roo.bootstrap.MessageBar
28475 * @extends Roo.bootstrap.Component
28476 * Bootstrap MessageBar class
28477 * @cfg {String} html contents of the MessageBar
28478 * @cfg {String} weight (info | success | warning | danger) default info
28479 * @cfg {String} beforeClass insert the bar before the given class
28480 * @cfg {Boolean} closable (true | false) default false
28481 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28484 * Create a new Element
28485 * @param {Object} config The config object
28488 Roo.bootstrap.MessageBar = function(config){
28489 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28492 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28498 beforeClass: 'bootstrap-sticky-wrap',
28500 getAutoCreate : function(){
28504 cls: 'alert alert-dismissable alert-' + this.weight,
28509 html: this.html || ''
28515 cfg.cls += ' alert-messages-fixed';
28529 onRender : function(ct, position)
28531 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28534 var cfg = Roo.apply({}, this.getAutoCreate());
28538 cfg.cls += ' ' + this.cls;
28541 cfg.style = this.style;
28543 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28545 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28548 this.el.select('>button.close').on('click', this.hide, this);
28554 if (!this.rendered) {
28560 this.fireEvent('show', this);
28566 if (!this.rendered) {
28572 this.fireEvent('hide', this);
28575 update : function()
28577 // var e = this.el.dom.firstChild;
28579 // if(this.closable){
28580 // e = e.nextSibling;
28583 // e.data = this.html || '';
28585 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28601 * @class Roo.bootstrap.Graph
28602 * @extends Roo.bootstrap.Component
28603 * Bootstrap Graph class
28607 @cfg {String} graphtype bar | vbar | pie
28608 @cfg {number} g_x coodinator | centre x (pie)
28609 @cfg {number} g_y coodinator | centre y (pie)
28610 @cfg {number} g_r radius (pie)
28611 @cfg {number} g_height height of the chart (respected by all elements in the set)
28612 @cfg {number} g_width width of the chart (respected by all elements in the set)
28613 @cfg {Object} title The title of the chart
28616 -opts (object) options for the chart
28618 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28619 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28621 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.
28622 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28624 o stretch (boolean)
28626 -opts (object) options for the pie
28629 o startAngle (number)
28630 o endAngle (number)
28634 * Create a new Input
28635 * @param {Object} config The config object
28638 Roo.bootstrap.Graph = function(config){
28639 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28645 * The img click event for the img.
28646 * @param {Roo.EventObject} e
28652 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28663 //g_colors: this.colors,
28670 getAutoCreate : function(){
28681 onRender : function(ct,position){
28684 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28686 if (typeof(Raphael) == 'undefined') {
28687 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28691 this.raphael = Raphael(this.el.dom);
28693 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28694 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28695 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28696 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28698 r.text(160, 10, "Single Series Chart").attr(txtattr);
28699 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28700 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28701 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28703 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28704 r.barchart(330, 10, 300, 220, data1);
28705 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28706 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28709 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28710 // r.barchart(30, 30, 560, 250, xdata, {
28711 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28712 // axis : "0 0 1 1",
28713 // axisxlabels : xdata
28714 // //yvalues : cols,
28717 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28719 // this.load(null,xdata,{
28720 // axis : "0 0 1 1",
28721 // axisxlabels : xdata
28726 load : function(graphtype,xdata,opts)
28728 this.raphael.clear();
28730 graphtype = this.graphtype;
28735 var r = this.raphael,
28736 fin = function () {
28737 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28739 fout = function () {
28740 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28742 pfin = function() {
28743 this.sector.stop();
28744 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28747 this.label[0].stop();
28748 this.label[0].attr({ r: 7.5 });
28749 this.label[1].attr({ "font-weight": 800 });
28752 pfout = function() {
28753 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28756 this.label[0].animate({ r: 5 }, 500, "bounce");
28757 this.label[1].attr({ "font-weight": 400 });
28763 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28766 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28769 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28770 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28772 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28779 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28784 setTitle: function(o)
28789 initEvents: function() {
28792 this.el.on('click', this.onClick, this);
28796 onClick : function(e)
28798 Roo.log('img onclick');
28799 this.fireEvent('click', this, e);
28811 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28814 * @class Roo.bootstrap.dash.NumberBox
28815 * @extends Roo.bootstrap.Component
28816 * Bootstrap NumberBox class
28817 * @cfg {String} headline Box headline
28818 * @cfg {String} content Box content
28819 * @cfg {String} icon Box icon
28820 * @cfg {String} footer Footer text
28821 * @cfg {String} fhref Footer href
28824 * Create a new NumberBox
28825 * @param {Object} config The config object
28829 Roo.bootstrap.dash.NumberBox = function(config){
28830 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28834 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28843 getAutoCreate : function(){
28847 cls : 'small-box ',
28855 cls : 'roo-headline',
28856 html : this.headline
28860 cls : 'roo-content',
28861 html : this.content
28875 cls : 'ion ' + this.icon
28884 cls : 'small-box-footer',
28885 href : this.fhref || '#',
28889 cfg.cn.push(footer);
28896 onRender : function(ct,position){
28897 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28904 setHeadline: function (value)
28906 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28909 setFooter: function (value, href)
28911 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28914 this.el.select('a.small-box-footer',true).first().attr('href', href);
28919 setContent: function (value)
28921 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28924 initEvents: function()
28938 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28941 * @class Roo.bootstrap.dash.TabBox
28942 * @extends Roo.bootstrap.Component
28943 * Bootstrap TabBox class
28944 * @cfg {String} title Title of the TabBox
28945 * @cfg {String} icon Icon of the TabBox
28946 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28947 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28950 * Create a new TabBox
28951 * @param {Object} config The config object
28955 Roo.bootstrap.dash.TabBox = function(config){
28956 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28961 * When a pane is added
28962 * @param {Roo.bootstrap.dash.TabPane} pane
28966 * @event activatepane
28967 * When a pane is activated
28968 * @param {Roo.bootstrap.dash.TabPane} pane
28970 "activatepane" : true
28978 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28983 tabScrollable : false,
28985 getChildContainer : function()
28987 return this.el.select('.tab-content', true).first();
28990 getAutoCreate : function(){
28994 cls: 'pull-left header',
29002 cls: 'fa ' + this.icon
29008 cls: 'nav nav-tabs pull-right',
29014 if(this.tabScrollable){
29021 cls: 'nav nav-tabs pull-right',
29032 cls: 'nav-tabs-custom',
29037 cls: 'tab-content no-padding',
29045 initEvents : function()
29047 //Roo.log('add add pane handler');
29048 this.on('addpane', this.onAddPane, this);
29051 * Updates the box title
29052 * @param {String} html to set the title to.
29054 setTitle : function(value)
29056 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29058 onAddPane : function(pane)
29060 this.panes.push(pane);
29061 //Roo.log('addpane');
29063 // tabs are rendere left to right..
29064 if(!this.showtabs){
29068 var ctr = this.el.select('.nav-tabs', true).first();
29071 var existing = ctr.select('.nav-tab',true);
29072 var qty = existing.getCount();;
29075 var tab = ctr.createChild({
29077 cls : 'nav-tab' + (qty ? '' : ' active'),
29085 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29088 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29090 pane.el.addClass('active');
29095 onTabClick : function(ev,un,ob,pane)
29097 //Roo.log('tab - prev default');
29098 ev.preventDefault();
29101 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29102 pane.tab.addClass('active');
29103 //Roo.log(pane.title);
29104 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29105 // technically we should have a deactivate event.. but maybe add later.
29106 // and it should not de-activate the selected tab...
29107 this.fireEvent('activatepane', pane);
29108 pane.el.addClass('active');
29109 pane.fireEvent('activate');
29114 getActivePane : function()
29117 Roo.each(this.panes, function(p) {
29118 if(p.el.hasClass('active')){
29139 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29141 * @class Roo.bootstrap.TabPane
29142 * @extends Roo.bootstrap.Component
29143 * Bootstrap TabPane class
29144 * @cfg {Boolean} active (false | true) Default false
29145 * @cfg {String} title title of panel
29149 * Create a new TabPane
29150 * @param {Object} config The config object
29153 Roo.bootstrap.dash.TabPane = function(config){
29154 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29160 * When a pane is activated
29161 * @param {Roo.bootstrap.dash.TabPane} pane
29168 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29173 // the tabBox that this is attached to.
29176 getAutoCreate : function()
29184 cfg.cls += ' active';
29189 initEvents : function()
29191 //Roo.log('trigger add pane handler');
29192 this.parent().fireEvent('addpane', this)
29196 * Updates the tab title
29197 * @param {String} html to set the title to.
29199 setTitle: function(str)
29205 this.tab.select('a', true).first().dom.innerHTML = str;
29222 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29225 * @class Roo.bootstrap.menu.Menu
29226 * @extends Roo.bootstrap.Component
29227 * Bootstrap Menu class - container for Menu
29228 * @cfg {String} html Text of the menu
29229 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29230 * @cfg {String} icon Font awesome icon
29231 * @cfg {String} pos Menu align to (top | bottom) default bottom
29235 * Create a new Menu
29236 * @param {Object} config The config object
29240 Roo.bootstrap.menu.Menu = function(config){
29241 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29245 * @event beforeshow
29246 * Fires before this menu is displayed
29247 * @param {Roo.bootstrap.menu.Menu} this
29251 * @event beforehide
29252 * Fires before this menu is hidden
29253 * @param {Roo.bootstrap.menu.Menu} this
29258 * Fires after this menu is displayed
29259 * @param {Roo.bootstrap.menu.Menu} this
29264 * Fires after this menu is hidden
29265 * @param {Roo.bootstrap.menu.Menu} this
29270 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29271 * @param {Roo.bootstrap.menu.Menu} this
29272 * @param {Roo.EventObject} e
29279 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
29283 weight : 'default',
29288 getChildContainer : function() {
29289 if(this.isSubMenu){
29293 return this.el.select('ul.dropdown-menu', true).first();
29296 getAutoCreate : function()
29301 cls : 'roo-menu-text',
29309 cls : 'fa ' + this.icon
29320 cls : 'dropdown-button btn btn-' + this.weight,
29325 cls : 'dropdown-toggle btn btn-' + this.weight,
29335 cls : 'dropdown-menu'
29341 if(this.pos == 'top'){
29342 cfg.cls += ' dropup';
29345 if(this.isSubMenu){
29348 cls : 'dropdown-menu'
29355 onRender : function(ct, position)
29357 this.isSubMenu = ct.hasClass('dropdown-submenu');
29359 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29362 initEvents : function()
29364 if(this.isSubMenu){
29368 this.hidden = true;
29370 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29371 this.triggerEl.on('click', this.onTriggerPress, this);
29373 this.buttonEl = this.el.select('button.dropdown-button', true).first();
29374 this.buttonEl.on('click', this.onClick, this);
29380 if(this.isSubMenu){
29384 return this.el.select('ul.dropdown-menu', true).first();
29387 onClick : function(e)
29389 this.fireEvent("click", this, e);
29392 onTriggerPress : function(e)
29394 if (this.isVisible()) {
29401 isVisible : function(){
29402 return !this.hidden;
29407 this.fireEvent("beforeshow", this);
29409 this.hidden = false;
29410 this.el.addClass('open');
29412 Roo.get(document).on("mouseup", this.onMouseUp, this);
29414 this.fireEvent("show", this);
29421 this.fireEvent("beforehide", this);
29423 this.hidden = true;
29424 this.el.removeClass('open');
29426 Roo.get(document).un("mouseup", this.onMouseUp);
29428 this.fireEvent("hide", this);
29431 onMouseUp : function()
29445 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29448 * @class Roo.bootstrap.menu.Item
29449 * @extends Roo.bootstrap.Component
29450 * Bootstrap MenuItem class
29451 * @cfg {Boolean} submenu (true | false) default false
29452 * @cfg {String} html text of the item
29453 * @cfg {String} href the link
29454 * @cfg {Boolean} disable (true | false) default false
29455 * @cfg {Boolean} preventDefault (true | false) default true
29456 * @cfg {String} icon Font awesome icon
29457 * @cfg {String} pos Submenu align to (left | right) default right
29461 * Create a new Item
29462 * @param {Object} config The config object
29466 Roo.bootstrap.menu.Item = function(config){
29467 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29471 * Fires when the mouse is hovering over this menu
29472 * @param {Roo.bootstrap.menu.Item} this
29473 * @param {Roo.EventObject} e
29478 * Fires when the mouse exits this menu
29479 * @param {Roo.bootstrap.menu.Item} this
29480 * @param {Roo.EventObject} e
29486 * The raw click event for the entire grid.
29487 * @param {Roo.EventObject} e
29493 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
29498 preventDefault: true,
29503 getAutoCreate : function()
29508 cls : 'roo-menu-item-text',
29516 cls : 'fa ' + this.icon
29525 href : this.href || '#',
29532 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29536 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29538 if(this.pos == 'left'){
29539 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29546 initEvents : function()
29548 this.el.on('mouseover', this.onMouseOver, this);
29549 this.el.on('mouseout', this.onMouseOut, this);
29551 this.el.select('a', true).first().on('click', this.onClick, this);
29555 onClick : function(e)
29557 if(this.preventDefault){
29558 e.preventDefault();
29561 this.fireEvent("click", this, e);
29564 onMouseOver : function(e)
29566 if(this.submenu && this.pos == 'left'){
29567 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29570 this.fireEvent("mouseover", this, e);
29573 onMouseOut : function(e)
29575 this.fireEvent("mouseout", this, e);
29587 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29590 * @class Roo.bootstrap.menu.Separator
29591 * @extends Roo.bootstrap.Component
29592 * Bootstrap Separator class
29595 * Create a new Separator
29596 * @param {Object} config The config object
29600 Roo.bootstrap.menu.Separator = function(config){
29601 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29604 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29606 getAutoCreate : function(){
29609 cls: 'dropdown-divider divider'
29627 * @class Roo.bootstrap.Tooltip
29628 * Bootstrap Tooltip class
29629 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29630 * to determine which dom element triggers the tooltip.
29632 * It needs to add support for additional attributes like tooltip-position
29635 * Create a new Toolti
29636 * @param {Object} config The config object
29639 Roo.bootstrap.Tooltip = function(config){
29640 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29642 this.alignment = Roo.bootstrap.Tooltip.alignment;
29644 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29645 this.alignment = config.alignment;
29650 Roo.apply(Roo.bootstrap.Tooltip, {
29652 * @function init initialize tooltip monitoring.
29656 currentTip : false,
29657 currentRegion : false,
29663 Roo.get(document).on('mouseover', this.enter ,this);
29664 Roo.get(document).on('mouseout', this.leave, this);
29667 this.currentTip = new Roo.bootstrap.Tooltip();
29670 enter : function(ev)
29672 var dom = ev.getTarget();
29674 //Roo.log(['enter',dom]);
29675 var el = Roo.fly(dom);
29676 if (this.currentEl) {
29678 //Roo.log(this.currentEl);
29679 //Roo.log(this.currentEl.contains(dom));
29680 if (this.currentEl == el) {
29683 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29689 if (this.currentTip.el) {
29690 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29694 if(!el || el.dom == document){
29700 if (!el.attr('tooltip')) {
29701 pel = el.findParent("[tooltip]");
29703 bindEl = Roo.get(pel);
29709 // you can not look for children, as if el is the body.. then everythign is the child..
29710 if (!pel && !el.attr('tooltip')) { //
29711 if (!el.select("[tooltip]").elements.length) {
29714 // is the mouse over this child...?
29715 bindEl = el.select("[tooltip]").first();
29716 var xy = ev.getXY();
29717 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29718 //Roo.log("not in region.");
29721 //Roo.log("child element over..");
29724 this.currentEl = el;
29725 this.currentTip.bind(bindEl);
29726 this.currentRegion = Roo.lib.Region.getRegion(dom);
29727 this.currentTip.enter();
29730 leave : function(ev)
29732 var dom = ev.getTarget();
29733 //Roo.log(['leave',dom]);
29734 if (!this.currentEl) {
29739 if (dom != this.currentEl.dom) {
29742 var xy = ev.getXY();
29743 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29746 // only activate leave if mouse cursor is outside... bounding box..
29751 if (this.currentTip) {
29752 this.currentTip.leave();
29754 //Roo.log('clear currentEl');
29755 this.currentEl = false;
29760 'left' : ['r-l', [-2,0], 'right'],
29761 'right' : ['l-r', [2,0], 'left'],
29762 'bottom' : ['t-b', [0,2], 'top'],
29763 'top' : [ 'b-t', [0,-2], 'bottom']
29769 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29774 delay : null, // can be { show : 300 , hide: 500}
29778 hoverState : null, //???
29780 placement : 'bottom',
29784 getAutoCreate : function(){
29791 cls : 'tooltip-arrow arrow'
29794 cls : 'tooltip-inner'
29801 bind : function(el)
29806 initEvents : function()
29808 this.arrowEl = this.el.select('.arrow', true).first();
29809 this.innerEl = this.el.select('.tooltip-inner', true).first();
29812 enter : function () {
29814 if (this.timeout != null) {
29815 clearTimeout(this.timeout);
29818 this.hoverState = 'in';
29819 //Roo.log("enter - show");
29820 if (!this.delay || !this.delay.show) {
29825 this.timeout = setTimeout(function () {
29826 if (_t.hoverState == 'in') {
29829 }, this.delay.show);
29833 clearTimeout(this.timeout);
29835 this.hoverState = 'out';
29836 if (!this.delay || !this.delay.hide) {
29842 this.timeout = setTimeout(function () {
29843 //Roo.log("leave - timeout");
29845 if (_t.hoverState == 'out') {
29847 Roo.bootstrap.Tooltip.currentEl = false;
29852 show : function (msg)
29855 this.render(document.body);
29858 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29860 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29862 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29864 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29865 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29867 var placement = typeof this.placement == 'function' ?
29868 this.placement.call(this, this.el, on_el) :
29871 var autoToken = /\s?auto?\s?/i;
29872 var autoPlace = autoToken.test(placement);
29874 placement = placement.replace(autoToken, '') || 'top';
29878 //this.el.setXY([0,0]);
29880 //this.el.dom.style.display='block';
29882 //this.el.appendTo(on_el);
29884 var p = this.getPosition();
29885 var box = this.el.getBox();
29891 var align = this.alignment[placement];
29893 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29895 if(placement == 'top' || placement == 'bottom'){
29897 placement = 'right';
29900 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29901 placement = 'left';
29904 var scroll = Roo.select('body', true).first().getScroll();
29906 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29910 align = this.alignment[placement];
29912 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29916 var elems = document.getElementsByTagName('div');
29917 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29918 for (var i = 0; i < elems.length; i++) {
29919 var zindex = Number.parseInt(
29920 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29923 if (zindex > highest) {
29930 this.el.dom.style.zIndex = highest;
29932 this.el.alignTo(this.bindEl, align[0],align[1]);
29933 //var arrow = this.el.select('.arrow',true).first();
29934 //arrow.set(align[2],
29936 this.el.addClass(placement);
29937 this.el.addClass("bs-tooltip-"+ placement);
29939 this.el.addClass('in fade show');
29941 this.hoverState = null;
29943 if (this.el.hasClass('fade')) {
29958 //this.el.setXY([0,0]);
29959 this.el.removeClass(['show', 'in']);
29975 * @class Roo.bootstrap.LocationPicker
29976 * @extends Roo.bootstrap.Component
29977 * Bootstrap LocationPicker class
29978 * @cfg {Number} latitude Position when init default 0
29979 * @cfg {Number} longitude Position when init default 0
29980 * @cfg {Number} zoom default 15
29981 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29982 * @cfg {Boolean} mapTypeControl default false
29983 * @cfg {Boolean} disableDoubleClickZoom default false
29984 * @cfg {Boolean} scrollwheel default true
29985 * @cfg {Boolean} streetViewControl default false
29986 * @cfg {Number} radius default 0
29987 * @cfg {String} locationName
29988 * @cfg {Boolean} draggable default true
29989 * @cfg {Boolean} enableAutocomplete default false
29990 * @cfg {Boolean} enableReverseGeocode default true
29991 * @cfg {String} markerTitle
29994 * Create a new LocationPicker
29995 * @param {Object} config The config object
29999 Roo.bootstrap.LocationPicker = function(config){
30001 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
30006 * Fires when the picker initialized.
30007 * @param {Roo.bootstrap.LocationPicker} this
30008 * @param {Google Location} location
30012 * @event positionchanged
30013 * Fires when the picker position changed.
30014 * @param {Roo.bootstrap.LocationPicker} this
30015 * @param {Google Location} location
30017 positionchanged : true,
30020 * Fires when the map resize.
30021 * @param {Roo.bootstrap.LocationPicker} this
30026 * Fires when the map show.
30027 * @param {Roo.bootstrap.LocationPicker} this
30032 * Fires when the map hide.
30033 * @param {Roo.bootstrap.LocationPicker} this
30038 * Fires when click the map.
30039 * @param {Roo.bootstrap.LocationPicker} this
30040 * @param {Map event} e
30044 * @event mapRightClick
30045 * Fires when right click the map.
30046 * @param {Roo.bootstrap.LocationPicker} this
30047 * @param {Map event} e
30049 mapRightClick : true,
30051 * @event markerClick
30052 * Fires when click the marker.
30053 * @param {Roo.bootstrap.LocationPicker} this
30054 * @param {Map event} e
30056 markerClick : true,
30058 * @event markerRightClick
30059 * Fires when right click the marker.
30060 * @param {Roo.bootstrap.LocationPicker} this
30061 * @param {Map event} e
30063 markerRightClick : true,
30065 * @event OverlayViewDraw
30066 * Fires when OverlayView Draw
30067 * @param {Roo.bootstrap.LocationPicker} this
30069 OverlayViewDraw : true,
30071 * @event OverlayViewOnAdd
30072 * Fires when OverlayView Draw
30073 * @param {Roo.bootstrap.LocationPicker} this
30075 OverlayViewOnAdd : true,
30077 * @event OverlayViewOnRemove
30078 * Fires when OverlayView Draw
30079 * @param {Roo.bootstrap.LocationPicker} this
30081 OverlayViewOnRemove : true,
30083 * @event OverlayViewShow
30084 * Fires when OverlayView Draw
30085 * @param {Roo.bootstrap.LocationPicker} this
30086 * @param {Pixel} cpx
30088 OverlayViewShow : true,
30090 * @event OverlayViewHide
30091 * Fires when OverlayView Draw
30092 * @param {Roo.bootstrap.LocationPicker} this
30094 OverlayViewHide : true,
30096 * @event loadexception
30097 * Fires when load google lib failed.
30098 * @param {Roo.bootstrap.LocationPicker} this
30100 loadexception : true
30105 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30107 gMapContext: false,
30113 mapTypeControl: false,
30114 disableDoubleClickZoom: false,
30116 streetViewControl: false,
30120 enableAutocomplete: false,
30121 enableReverseGeocode: true,
30124 getAutoCreate: function()
30129 cls: 'roo-location-picker'
30135 initEvents: function(ct, position)
30137 if(!this.el.getWidth() || this.isApplied()){
30141 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30146 initial: function()
30148 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30149 this.fireEvent('loadexception', this);
30153 if(!this.mapTypeId){
30154 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30157 this.gMapContext = this.GMapContext();
30159 this.initOverlayView();
30161 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30165 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30166 _this.setPosition(_this.gMapContext.marker.position);
30169 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30170 _this.fireEvent('mapClick', this, event);
30174 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30175 _this.fireEvent('mapRightClick', this, event);
30179 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30180 _this.fireEvent('markerClick', this, event);
30184 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30185 _this.fireEvent('markerRightClick', this, event);
30189 this.setPosition(this.gMapContext.location);
30191 this.fireEvent('initial', this, this.gMapContext.location);
30194 initOverlayView: function()
30198 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30202 _this.fireEvent('OverlayViewDraw', _this);
30207 _this.fireEvent('OverlayViewOnAdd', _this);
30210 onRemove: function()
30212 _this.fireEvent('OverlayViewOnRemove', _this);
30215 show: function(cpx)
30217 _this.fireEvent('OverlayViewShow', _this, cpx);
30222 _this.fireEvent('OverlayViewHide', _this);
30228 fromLatLngToContainerPixel: function(event)
30230 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30233 isApplied: function()
30235 return this.getGmapContext() == false ? false : true;
30238 getGmapContext: function()
30240 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30243 GMapContext: function()
30245 var position = new google.maps.LatLng(this.latitude, this.longitude);
30247 var _map = new google.maps.Map(this.el.dom, {
30250 mapTypeId: this.mapTypeId,
30251 mapTypeControl: this.mapTypeControl,
30252 disableDoubleClickZoom: this.disableDoubleClickZoom,
30253 scrollwheel: this.scrollwheel,
30254 streetViewControl: this.streetViewControl,
30255 locationName: this.locationName,
30256 draggable: this.draggable,
30257 enableAutocomplete: this.enableAutocomplete,
30258 enableReverseGeocode: this.enableReverseGeocode
30261 var _marker = new google.maps.Marker({
30262 position: position,
30264 title: this.markerTitle,
30265 draggable: this.draggable
30272 location: position,
30273 radius: this.radius,
30274 locationName: this.locationName,
30275 addressComponents: {
30276 formatted_address: null,
30277 addressLine1: null,
30278 addressLine2: null,
30280 streetNumber: null,
30284 stateOrProvince: null
30287 domContainer: this.el.dom,
30288 geodecoder: new google.maps.Geocoder()
30292 drawCircle: function(center, radius, options)
30294 if (this.gMapContext.circle != null) {
30295 this.gMapContext.circle.setMap(null);
30299 options = Roo.apply({}, options, {
30300 strokeColor: "#0000FF",
30301 strokeOpacity: .35,
30303 fillColor: "#0000FF",
30307 options.map = this.gMapContext.map;
30308 options.radius = radius;
30309 options.center = center;
30310 this.gMapContext.circle = new google.maps.Circle(options);
30311 return this.gMapContext.circle;
30317 setPosition: function(location)
30319 this.gMapContext.location = location;
30320 this.gMapContext.marker.setPosition(location);
30321 this.gMapContext.map.panTo(location);
30322 this.drawCircle(location, this.gMapContext.radius, {});
30326 if (this.gMapContext.settings.enableReverseGeocode) {
30327 this.gMapContext.geodecoder.geocode({
30328 latLng: this.gMapContext.location
30329 }, function(results, status) {
30331 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30332 _this.gMapContext.locationName = results[0].formatted_address;
30333 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30335 _this.fireEvent('positionchanged', this, location);
30342 this.fireEvent('positionchanged', this, location);
30347 google.maps.event.trigger(this.gMapContext.map, "resize");
30349 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30351 this.fireEvent('resize', this);
30354 setPositionByLatLng: function(latitude, longitude)
30356 this.setPosition(new google.maps.LatLng(latitude, longitude));
30359 getCurrentPosition: function()
30362 latitude: this.gMapContext.location.lat(),
30363 longitude: this.gMapContext.location.lng()
30367 getAddressName: function()
30369 return this.gMapContext.locationName;
30372 getAddressComponents: function()
30374 return this.gMapContext.addressComponents;
30377 address_component_from_google_geocode: function(address_components)
30381 for (var i = 0; i < address_components.length; i++) {
30382 var component = address_components[i];
30383 if (component.types.indexOf("postal_code") >= 0) {
30384 result.postalCode = component.short_name;
30385 } else if (component.types.indexOf("street_number") >= 0) {
30386 result.streetNumber = component.short_name;
30387 } else if (component.types.indexOf("route") >= 0) {
30388 result.streetName = component.short_name;
30389 } else if (component.types.indexOf("neighborhood") >= 0) {
30390 result.city = component.short_name;
30391 } else if (component.types.indexOf("locality") >= 0) {
30392 result.city = component.short_name;
30393 } else if (component.types.indexOf("sublocality") >= 0) {
30394 result.district = component.short_name;
30395 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30396 result.stateOrProvince = component.short_name;
30397 } else if (component.types.indexOf("country") >= 0) {
30398 result.country = component.short_name;
30402 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30403 result.addressLine2 = "";
30407 setZoomLevel: function(zoom)
30409 this.gMapContext.map.setZoom(zoom);
30422 this.fireEvent('show', this);
30433 this.fireEvent('hide', this);
30438 Roo.apply(Roo.bootstrap.LocationPicker, {
30440 OverlayView : function(map, options)
30442 options = options || {};
30449 * @class Roo.bootstrap.Alert
30450 * @extends Roo.bootstrap.Component
30451 * Bootstrap Alert class - shows an alert area box
30453 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30454 Enter a valid email address
30457 * @cfg {String} title The title of alert
30458 * @cfg {String} html The content of alert
30459 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30460 * @cfg {String} fa font-awesomeicon
30461 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30462 * @cfg {Boolean} close true to show a x closer
30466 * Create a new alert
30467 * @param {Object} config The config object
30471 Roo.bootstrap.Alert = function(config){
30472 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30476 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30482 faicon: false, // BC
30486 getAutoCreate : function()
30498 style : this.close ? '' : 'display:none'
30502 cls : 'roo-alert-icon'
30507 cls : 'roo-alert-title',
30512 cls : 'roo-alert-text',
30519 cfg.cn[0].cls += ' fa ' + this.faicon;
30522 cfg.cn[0].cls += ' fa ' + this.fa;
30526 cfg.cls += ' alert-' + this.weight;
30532 initEvents: function()
30534 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30535 this.titleEl = this.el.select('.roo-alert-title',true).first();
30536 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30537 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30538 if (this.seconds > 0) {
30539 this.hide.defer(this.seconds, this);
30543 * Set the Title Message HTML
30544 * @param {String} html
30546 setTitle : function(str)
30548 this.titleEl.dom.innerHTML = str;
30552 * Set the Body Message HTML
30553 * @param {String} html
30555 setHtml : function(str)
30557 this.htmlEl.dom.innerHTML = str;
30560 * Set the Weight of the alert
30561 * @param {String} (success|info|warning|danger) weight
30564 setWeight : function(weight)
30567 this.el.removeClass('alert-' + this.weight);
30570 this.weight = weight;
30572 this.el.addClass('alert-' + this.weight);
30575 * Set the Icon of the alert
30576 * @param {String} see fontawsome names (name without the 'fa-' bit)
30578 setIcon : function(icon)
30581 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30584 this.faicon = icon;
30586 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30611 * @class Roo.bootstrap.UploadCropbox
30612 * @extends Roo.bootstrap.Component
30613 * Bootstrap UploadCropbox class
30614 * @cfg {String} emptyText show when image has been loaded
30615 * @cfg {String} rotateNotify show when image too small to rotate
30616 * @cfg {Number} errorTimeout default 3000
30617 * @cfg {Number} minWidth default 300
30618 * @cfg {Number} minHeight default 300
30619 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30620 * @cfg {Boolean} isDocument (true|false) default false
30621 * @cfg {String} url action url
30622 * @cfg {String} paramName default 'imageUpload'
30623 * @cfg {String} method default POST
30624 * @cfg {Boolean} loadMask (true|false) default true
30625 * @cfg {Boolean} loadingText default 'Loading...'
30628 * Create a new UploadCropbox
30629 * @param {Object} config The config object
30632 Roo.bootstrap.UploadCropbox = function(config){
30633 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30637 * @event beforeselectfile
30638 * Fire before select file
30639 * @param {Roo.bootstrap.UploadCropbox} this
30641 "beforeselectfile" : true,
30644 * Fire after initEvent
30645 * @param {Roo.bootstrap.UploadCropbox} this
30650 * Fire after initEvent
30651 * @param {Roo.bootstrap.UploadCropbox} this
30652 * @param {String} data
30657 * Fire when preparing the file data
30658 * @param {Roo.bootstrap.UploadCropbox} this
30659 * @param {Object} file
30664 * Fire when get exception
30665 * @param {Roo.bootstrap.UploadCropbox} this
30666 * @param {XMLHttpRequest} xhr
30668 "exception" : true,
30670 * @event beforeloadcanvas
30671 * Fire before load the canvas
30672 * @param {Roo.bootstrap.UploadCropbox} this
30673 * @param {String} src
30675 "beforeloadcanvas" : true,
30678 * Fire when trash image
30679 * @param {Roo.bootstrap.UploadCropbox} this
30684 * Fire when download the image
30685 * @param {Roo.bootstrap.UploadCropbox} this
30689 * @event footerbuttonclick
30690 * Fire when footerbuttonclick
30691 * @param {Roo.bootstrap.UploadCropbox} this
30692 * @param {String} type
30694 "footerbuttonclick" : true,
30698 * @param {Roo.bootstrap.UploadCropbox} this
30703 * Fire when rotate the image
30704 * @param {Roo.bootstrap.UploadCropbox} this
30705 * @param {String} pos
30710 * Fire when inspect the file
30711 * @param {Roo.bootstrap.UploadCropbox} this
30712 * @param {Object} file
30717 * Fire when xhr upload the file
30718 * @param {Roo.bootstrap.UploadCropbox} this
30719 * @param {Object} data
30724 * Fire when arrange the file data
30725 * @param {Roo.bootstrap.UploadCropbox} this
30726 * @param {Object} formData
30731 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30734 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30736 emptyText : 'Click to upload image',
30737 rotateNotify : 'Image is too small to rotate',
30738 errorTimeout : 3000,
30752 cropType : 'image/jpeg',
30754 canvasLoaded : false,
30755 isDocument : false,
30757 paramName : 'imageUpload',
30759 loadingText : 'Loading...',
30762 getAutoCreate : function()
30766 cls : 'roo-upload-cropbox',
30770 cls : 'roo-upload-cropbox-selector',
30775 cls : 'roo-upload-cropbox-body',
30776 style : 'cursor:pointer',
30780 cls : 'roo-upload-cropbox-preview'
30784 cls : 'roo-upload-cropbox-thumb'
30788 cls : 'roo-upload-cropbox-empty-notify',
30789 html : this.emptyText
30793 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30794 html : this.rotateNotify
30800 cls : 'roo-upload-cropbox-footer',
30803 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30813 onRender : function(ct, position)
30815 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30817 if (this.buttons.length) {
30819 Roo.each(this.buttons, function(bb) {
30821 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30823 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30829 this.maskEl = this.el;
30833 initEvents : function()
30835 this.urlAPI = (window.createObjectURL && window) ||
30836 (window.URL && URL.revokeObjectURL && URL) ||
30837 (window.webkitURL && webkitURL);
30839 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30840 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30842 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30843 this.selectorEl.hide();
30845 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30846 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30848 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30849 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30850 this.thumbEl.hide();
30852 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30853 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30855 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30856 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30857 this.errorEl.hide();
30859 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30860 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30861 this.footerEl.hide();
30863 this.setThumbBoxSize();
30869 this.fireEvent('initial', this);
30876 window.addEventListener("resize", function() { _this.resize(); } );
30878 this.bodyEl.on('click', this.beforeSelectFile, this);
30881 this.bodyEl.on('touchstart', this.onTouchStart, this);
30882 this.bodyEl.on('touchmove', this.onTouchMove, this);
30883 this.bodyEl.on('touchend', this.onTouchEnd, this);
30887 this.bodyEl.on('mousedown', this.onMouseDown, this);
30888 this.bodyEl.on('mousemove', this.onMouseMove, this);
30889 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30890 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30891 Roo.get(document).on('mouseup', this.onMouseUp, this);
30894 this.selectorEl.on('change', this.onFileSelected, this);
30900 this.baseScale = 1;
30902 this.baseRotate = 1;
30903 this.dragable = false;
30904 this.pinching = false;
30907 this.cropData = false;
30908 this.notifyEl.dom.innerHTML = this.emptyText;
30910 this.selectorEl.dom.value = '';
30914 resize : function()
30916 if(this.fireEvent('resize', this) != false){
30917 this.setThumbBoxPosition();
30918 this.setCanvasPosition();
30922 onFooterButtonClick : function(e, el, o, type)
30925 case 'rotate-left' :
30926 this.onRotateLeft(e);
30928 case 'rotate-right' :
30929 this.onRotateRight(e);
30932 this.beforeSelectFile(e);
30947 this.fireEvent('footerbuttonclick', this, type);
30950 beforeSelectFile : function(e)
30952 e.preventDefault();
30954 if(this.fireEvent('beforeselectfile', this) != false){
30955 this.selectorEl.dom.click();
30959 onFileSelected : function(e)
30961 e.preventDefault();
30963 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30967 var file = this.selectorEl.dom.files[0];
30969 if(this.fireEvent('inspect', this, file) != false){
30970 this.prepare(file);
30975 trash : function(e)
30977 this.fireEvent('trash', this);
30980 download : function(e)
30982 this.fireEvent('download', this);
30985 loadCanvas : function(src)
30987 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30991 this.imageEl = document.createElement('img');
30995 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30997 this.imageEl.src = src;
31001 onLoadCanvas : function()
31003 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
31004 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
31006 this.bodyEl.un('click', this.beforeSelectFile, this);
31008 this.notifyEl.hide();
31009 this.thumbEl.show();
31010 this.footerEl.show();
31012 this.baseRotateLevel();
31014 if(this.isDocument){
31015 this.setThumbBoxSize();
31018 this.setThumbBoxPosition();
31020 this.baseScaleLevel();
31026 this.canvasLoaded = true;
31029 this.maskEl.unmask();
31034 setCanvasPosition : function()
31036 if(!this.canvasEl){
31040 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31041 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31043 this.previewEl.setLeft(pw);
31044 this.previewEl.setTop(ph);
31048 onMouseDown : function(e)
31052 this.dragable = true;
31053 this.pinching = false;
31055 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31056 this.dragable = false;
31060 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31061 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31065 onMouseMove : function(e)
31069 if(!this.canvasLoaded){
31073 if (!this.dragable){
31077 var minX = Math.ceil(this.thumbEl.getLeft(true));
31078 var minY = Math.ceil(this.thumbEl.getTop(true));
31080 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31081 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31083 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31084 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31086 x = x - this.mouseX;
31087 y = y - this.mouseY;
31089 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31090 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31092 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31093 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31095 this.previewEl.setLeft(bgX);
31096 this.previewEl.setTop(bgY);
31098 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31099 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31102 onMouseUp : function(e)
31106 this.dragable = false;
31109 onMouseWheel : function(e)
31113 this.startScale = this.scale;
31115 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31117 if(!this.zoomable()){
31118 this.scale = this.startScale;
31127 zoomable : function()
31129 var minScale = this.thumbEl.getWidth() / this.minWidth;
31131 if(this.minWidth < this.minHeight){
31132 minScale = this.thumbEl.getHeight() / this.minHeight;
31135 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31136 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31140 (this.rotate == 0 || this.rotate == 180) &&
31142 width > this.imageEl.OriginWidth ||
31143 height > this.imageEl.OriginHeight ||
31144 (width < this.minWidth && height < this.minHeight)
31152 (this.rotate == 90 || this.rotate == 270) &&
31154 width > this.imageEl.OriginWidth ||
31155 height > this.imageEl.OriginHeight ||
31156 (width < this.minHeight && height < this.minWidth)
31163 !this.isDocument &&
31164 (this.rotate == 0 || this.rotate == 180) &&
31166 width < this.minWidth ||
31167 width > this.imageEl.OriginWidth ||
31168 height < this.minHeight ||
31169 height > this.imageEl.OriginHeight
31176 !this.isDocument &&
31177 (this.rotate == 90 || this.rotate == 270) &&
31179 width < this.minHeight ||
31180 width > this.imageEl.OriginWidth ||
31181 height < this.minWidth ||
31182 height > this.imageEl.OriginHeight
31192 onRotateLeft : function(e)
31194 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31196 var minScale = this.thumbEl.getWidth() / this.minWidth;
31198 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31199 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31201 this.startScale = this.scale;
31203 while (this.getScaleLevel() < minScale){
31205 this.scale = this.scale + 1;
31207 if(!this.zoomable()){
31212 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31213 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31218 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31225 this.scale = this.startScale;
31227 this.onRotateFail();
31232 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31234 if(this.isDocument){
31235 this.setThumbBoxSize();
31236 this.setThumbBoxPosition();
31237 this.setCanvasPosition();
31242 this.fireEvent('rotate', this, 'left');
31246 onRotateRight : function(e)
31248 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31250 var minScale = this.thumbEl.getWidth() / this.minWidth;
31252 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31253 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31255 this.startScale = this.scale;
31257 while (this.getScaleLevel() < minScale){
31259 this.scale = this.scale + 1;
31261 if(!this.zoomable()){
31266 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31267 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31272 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31279 this.scale = this.startScale;
31281 this.onRotateFail();
31286 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31288 if(this.isDocument){
31289 this.setThumbBoxSize();
31290 this.setThumbBoxPosition();
31291 this.setCanvasPosition();
31296 this.fireEvent('rotate', this, 'right');
31299 onRotateFail : function()
31301 this.errorEl.show(true);
31305 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31310 this.previewEl.dom.innerHTML = '';
31312 var canvasEl = document.createElement("canvas");
31314 var contextEl = canvasEl.getContext("2d");
31316 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31317 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31318 var center = this.imageEl.OriginWidth / 2;
31320 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31321 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31322 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31323 center = this.imageEl.OriginHeight / 2;
31326 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31328 contextEl.translate(center, center);
31329 contextEl.rotate(this.rotate * Math.PI / 180);
31331 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31333 this.canvasEl = document.createElement("canvas");
31335 this.contextEl = this.canvasEl.getContext("2d");
31337 switch (this.rotate) {
31340 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31341 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31343 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31348 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31349 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31351 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31352 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);
31356 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31361 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31362 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31364 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31365 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);
31369 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);
31374 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31375 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31377 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31378 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31382 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);
31389 this.previewEl.appendChild(this.canvasEl);
31391 this.setCanvasPosition();
31396 if(!this.canvasLoaded){
31400 var imageCanvas = document.createElement("canvas");
31402 var imageContext = imageCanvas.getContext("2d");
31404 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31405 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31407 var center = imageCanvas.width / 2;
31409 imageContext.translate(center, center);
31411 imageContext.rotate(this.rotate * Math.PI / 180);
31413 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31415 var canvas = document.createElement("canvas");
31417 var context = canvas.getContext("2d");
31419 canvas.width = this.minWidth;
31420 canvas.height = this.minHeight;
31422 switch (this.rotate) {
31425 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31426 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31428 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31429 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31431 var targetWidth = this.minWidth - 2 * x;
31432 var targetHeight = this.minHeight - 2 * y;
31436 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31437 scale = targetWidth / width;
31440 if(x > 0 && y == 0){
31441 scale = targetHeight / height;
31444 if(x > 0 && y > 0){
31445 scale = targetWidth / width;
31447 if(width < height){
31448 scale = targetHeight / height;
31452 context.scale(scale, scale);
31454 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31455 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31457 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31458 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31460 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31465 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31466 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31468 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31469 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31471 var targetWidth = this.minWidth - 2 * x;
31472 var targetHeight = this.minHeight - 2 * y;
31476 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31477 scale = targetWidth / width;
31480 if(x > 0 && y == 0){
31481 scale = targetHeight / height;
31484 if(x > 0 && y > 0){
31485 scale = targetWidth / width;
31487 if(width < height){
31488 scale = targetHeight / height;
31492 context.scale(scale, scale);
31494 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31495 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31497 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31498 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31500 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31502 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31507 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31508 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31510 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31511 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31513 var targetWidth = this.minWidth - 2 * x;
31514 var targetHeight = this.minHeight - 2 * y;
31518 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31519 scale = targetWidth / width;
31522 if(x > 0 && y == 0){
31523 scale = targetHeight / height;
31526 if(x > 0 && y > 0){
31527 scale = targetWidth / width;
31529 if(width < height){
31530 scale = targetHeight / height;
31534 context.scale(scale, scale);
31536 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31537 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31539 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31540 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31542 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31543 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31545 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31550 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31551 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31553 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31554 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31556 var targetWidth = this.minWidth - 2 * x;
31557 var targetHeight = this.minHeight - 2 * y;
31561 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31562 scale = targetWidth / width;
31565 if(x > 0 && y == 0){
31566 scale = targetHeight / height;
31569 if(x > 0 && y > 0){
31570 scale = targetWidth / width;
31572 if(width < height){
31573 scale = targetHeight / height;
31577 context.scale(scale, scale);
31579 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31580 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31582 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31583 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31585 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31587 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31594 this.cropData = canvas.toDataURL(this.cropType);
31596 if(this.fireEvent('crop', this, this.cropData) !== false){
31597 this.process(this.file, this.cropData);
31604 setThumbBoxSize : function()
31608 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31609 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31610 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31612 this.minWidth = width;
31613 this.minHeight = height;
31615 if(this.rotate == 90 || this.rotate == 270){
31616 this.minWidth = height;
31617 this.minHeight = width;
31622 width = Math.ceil(this.minWidth * height / this.minHeight);
31624 if(this.minWidth > this.minHeight){
31626 height = Math.ceil(this.minHeight * width / this.minWidth);
31629 this.thumbEl.setStyle({
31630 width : width + 'px',
31631 height : height + 'px'
31638 setThumbBoxPosition : function()
31640 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31641 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31643 this.thumbEl.setLeft(x);
31644 this.thumbEl.setTop(y);
31648 baseRotateLevel : function()
31650 this.baseRotate = 1;
31653 typeof(this.exif) != 'undefined' &&
31654 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31655 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31657 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31660 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31664 baseScaleLevel : function()
31668 if(this.isDocument){
31670 if(this.baseRotate == 6 || this.baseRotate == 8){
31672 height = this.thumbEl.getHeight();
31673 this.baseScale = height / this.imageEl.OriginWidth;
31675 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31676 width = this.thumbEl.getWidth();
31677 this.baseScale = width / this.imageEl.OriginHeight;
31683 height = this.thumbEl.getHeight();
31684 this.baseScale = height / this.imageEl.OriginHeight;
31686 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31687 width = this.thumbEl.getWidth();
31688 this.baseScale = width / this.imageEl.OriginWidth;
31694 if(this.baseRotate == 6 || this.baseRotate == 8){
31696 width = this.thumbEl.getHeight();
31697 this.baseScale = width / this.imageEl.OriginHeight;
31699 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31700 height = this.thumbEl.getWidth();
31701 this.baseScale = height / this.imageEl.OriginHeight;
31704 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31705 height = this.thumbEl.getWidth();
31706 this.baseScale = height / this.imageEl.OriginHeight;
31708 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31709 width = this.thumbEl.getHeight();
31710 this.baseScale = width / this.imageEl.OriginWidth;
31717 width = this.thumbEl.getWidth();
31718 this.baseScale = width / this.imageEl.OriginWidth;
31720 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31721 height = this.thumbEl.getHeight();
31722 this.baseScale = height / this.imageEl.OriginHeight;
31725 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31727 height = this.thumbEl.getHeight();
31728 this.baseScale = height / this.imageEl.OriginHeight;
31730 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31731 width = this.thumbEl.getWidth();
31732 this.baseScale = width / this.imageEl.OriginWidth;
31740 getScaleLevel : function()
31742 return this.baseScale * Math.pow(1.1, this.scale);
31745 onTouchStart : function(e)
31747 if(!this.canvasLoaded){
31748 this.beforeSelectFile(e);
31752 var touches = e.browserEvent.touches;
31758 if(touches.length == 1){
31759 this.onMouseDown(e);
31763 if(touches.length != 2){
31769 for(var i = 0, finger; finger = touches[i]; i++){
31770 coords.push(finger.pageX, finger.pageY);
31773 var x = Math.pow(coords[0] - coords[2], 2);
31774 var y = Math.pow(coords[1] - coords[3], 2);
31776 this.startDistance = Math.sqrt(x + y);
31778 this.startScale = this.scale;
31780 this.pinching = true;
31781 this.dragable = false;
31785 onTouchMove : function(e)
31787 if(!this.pinching && !this.dragable){
31791 var touches = e.browserEvent.touches;
31798 this.onMouseMove(e);
31804 for(var i = 0, finger; finger = touches[i]; i++){
31805 coords.push(finger.pageX, finger.pageY);
31808 var x = Math.pow(coords[0] - coords[2], 2);
31809 var y = Math.pow(coords[1] - coords[3], 2);
31811 this.endDistance = Math.sqrt(x + y);
31813 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31815 if(!this.zoomable()){
31816 this.scale = this.startScale;
31824 onTouchEnd : function(e)
31826 this.pinching = false;
31827 this.dragable = false;
31831 process : function(file, crop)
31834 this.maskEl.mask(this.loadingText);
31837 this.xhr = new XMLHttpRequest();
31839 file.xhr = this.xhr;
31841 this.xhr.open(this.method, this.url, true);
31844 "Accept": "application/json",
31845 "Cache-Control": "no-cache",
31846 "X-Requested-With": "XMLHttpRequest"
31849 for (var headerName in headers) {
31850 var headerValue = headers[headerName];
31852 this.xhr.setRequestHeader(headerName, headerValue);
31858 this.xhr.onload = function()
31860 _this.xhrOnLoad(_this.xhr);
31863 this.xhr.onerror = function()
31865 _this.xhrOnError(_this.xhr);
31868 var formData = new FormData();
31870 formData.append('returnHTML', 'NO');
31873 formData.append('crop', crop);
31876 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31877 formData.append(this.paramName, file, file.name);
31880 if(typeof(file.filename) != 'undefined'){
31881 formData.append('filename', file.filename);
31884 if(typeof(file.mimetype) != 'undefined'){
31885 formData.append('mimetype', file.mimetype);
31888 if(this.fireEvent('arrange', this, formData) != false){
31889 this.xhr.send(formData);
31893 xhrOnLoad : function(xhr)
31896 this.maskEl.unmask();
31899 if (xhr.readyState !== 4) {
31900 this.fireEvent('exception', this, xhr);
31904 var response = Roo.decode(xhr.responseText);
31906 if(!response.success){
31907 this.fireEvent('exception', this, xhr);
31911 var response = Roo.decode(xhr.responseText);
31913 this.fireEvent('upload', this, response);
31917 xhrOnError : function()
31920 this.maskEl.unmask();
31923 Roo.log('xhr on error');
31925 var response = Roo.decode(xhr.responseText);
31931 prepare : function(file)
31934 this.maskEl.mask(this.loadingText);
31940 if(typeof(file) === 'string'){
31941 this.loadCanvas(file);
31945 if(!file || !this.urlAPI){
31950 this.cropType = file.type;
31954 if(this.fireEvent('prepare', this, this.file) != false){
31956 var reader = new FileReader();
31958 reader.onload = function (e) {
31959 if (e.target.error) {
31960 Roo.log(e.target.error);
31964 var buffer = e.target.result,
31965 dataView = new DataView(buffer),
31967 maxOffset = dataView.byteLength - 4,
31971 if (dataView.getUint16(0) === 0xffd8) {
31972 while (offset < maxOffset) {
31973 markerBytes = dataView.getUint16(offset);
31975 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31976 markerLength = dataView.getUint16(offset + 2) + 2;
31977 if (offset + markerLength > dataView.byteLength) {
31978 Roo.log('Invalid meta data: Invalid segment size.');
31982 if(markerBytes == 0xffe1){
31983 _this.parseExifData(
31990 offset += markerLength;
32000 var url = _this.urlAPI.createObjectURL(_this.file);
32002 _this.loadCanvas(url);
32007 reader.readAsArrayBuffer(this.file);
32013 parseExifData : function(dataView, offset, length)
32015 var tiffOffset = offset + 10,
32019 if (dataView.getUint32(offset + 4) !== 0x45786966) {
32020 // No Exif data, might be XMP data instead
32024 // Check for the ASCII code for "Exif" (0x45786966):
32025 if (dataView.getUint32(offset + 4) !== 0x45786966) {
32026 // No Exif data, might be XMP data instead
32029 if (tiffOffset + 8 > dataView.byteLength) {
32030 Roo.log('Invalid Exif data: Invalid segment size.');
32033 // Check for the two null bytes:
32034 if (dataView.getUint16(offset + 8) !== 0x0000) {
32035 Roo.log('Invalid Exif data: Missing byte alignment offset.');
32038 // Check the byte alignment:
32039 switch (dataView.getUint16(tiffOffset)) {
32041 littleEndian = true;
32044 littleEndian = false;
32047 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32050 // Check for the TIFF tag marker (0x002A):
32051 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32052 Roo.log('Invalid Exif data: Missing TIFF marker.');
32055 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32056 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32058 this.parseExifTags(
32061 tiffOffset + dirOffset,
32066 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32071 if (dirOffset + 6 > dataView.byteLength) {
32072 Roo.log('Invalid Exif data: Invalid directory offset.');
32075 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32076 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32077 if (dirEndOffset + 4 > dataView.byteLength) {
32078 Roo.log('Invalid Exif data: Invalid directory size.');
32081 for (i = 0; i < tagsNumber; i += 1) {
32085 dirOffset + 2 + 12 * i, // tag offset
32089 // Return the offset to the next directory:
32090 return dataView.getUint32(dirEndOffset, littleEndian);
32093 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32095 var tag = dataView.getUint16(offset, littleEndian);
32097 this.exif[tag] = this.getExifValue(
32101 dataView.getUint16(offset + 2, littleEndian), // tag type
32102 dataView.getUint32(offset + 4, littleEndian), // tag length
32107 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32109 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32118 Roo.log('Invalid Exif data: Invalid tag type.');
32122 tagSize = tagType.size * length;
32123 // Determine if the value is contained in the dataOffset bytes,
32124 // or if the value at the dataOffset is a pointer to the actual data:
32125 dataOffset = tagSize > 4 ?
32126 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32127 if (dataOffset + tagSize > dataView.byteLength) {
32128 Roo.log('Invalid Exif data: Invalid data offset.');
32131 if (length === 1) {
32132 return tagType.getValue(dataView, dataOffset, littleEndian);
32135 for (i = 0; i < length; i += 1) {
32136 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32139 if (tagType.ascii) {
32141 // Concatenate the chars:
32142 for (i = 0; i < values.length; i += 1) {
32144 // Ignore the terminating NULL byte(s):
32145 if (c === '\u0000') {
32157 Roo.apply(Roo.bootstrap.UploadCropbox, {
32159 'Orientation': 0x0112
32163 1: 0, //'top-left',
32165 3: 180, //'bottom-right',
32166 // 4: 'bottom-left',
32168 6: 90, //'right-top',
32169 // 7: 'right-bottom',
32170 8: 270 //'left-bottom'
32174 // byte, 8-bit unsigned int:
32176 getValue: function (dataView, dataOffset) {
32177 return dataView.getUint8(dataOffset);
32181 // ascii, 8-bit byte:
32183 getValue: function (dataView, dataOffset) {
32184 return String.fromCharCode(dataView.getUint8(dataOffset));
32189 // short, 16 bit int:
32191 getValue: function (dataView, dataOffset, littleEndian) {
32192 return dataView.getUint16(dataOffset, littleEndian);
32196 // long, 32 bit int:
32198 getValue: function (dataView, dataOffset, littleEndian) {
32199 return dataView.getUint32(dataOffset, littleEndian);
32203 // rational = two long values, first is numerator, second is denominator:
32205 getValue: function (dataView, dataOffset, littleEndian) {
32206 return dataView.getUint32(dataOffset, littleEndian) /
32207 dataView.getUint32(dataOffset + 4, littleEndian);
32211 // slong, 32 bit signed int:
32213 getValue: function (dataView, dataOffset, littleEndian) {
32214 return dataView.getInt32(dataOffset, littleEndian);
32218 // srational, two slongs, first is numerator, second is denominator:
32220 getValue: function (dataView, dataOffset, littleEndian) {
32221 return dataView.getInt32(dataOffset, littleEndian) /
32222 dataView.getInt32(dataOffset + 4, littleEndian);
32232 cls : 'btn-group roo-upload-cropbox-rotate-left',
32233 action : 'rotate-left',
32237 cls : 'btn btn-default',
32238 html : '<i class="fa fa-undo"></i>'
32244 cls : 'btn-group roo-upload-cropbox-picture',
32245 action : 'picture',
32249 cls : 'btn btn-default',
32250 html : '<i class="fa fa-picture-o"></i>'
32256 cls : 'btn-group roo-upload-cropbox-rotate-right',
32257 action : 'rotate-right',
32261 cls : 'btn btn-default',
32262 html : '<i class="fa fa-repeat"></i>'
32270 cls : 'btn-group roo-upload-cropbox-rotate-left',
32271 action : 'rotate-left',
32275 cls : 'btn btn-default',
32276 html : '<i class="fa fa-undo"></i>'
32282 cls : 'btn-group roo-upload-cropbox-download',
32283 action : 'download',
32287 cls : 'btn btn-default',
32288 html : '<i class="fa fa-download"></i>'
32294 cls : 'btn-group roo-upload-cropbox-crop',
32299 cls : 'btn btn-default',
32300 html : '<i class="fa fa-crop"></i>'
32306 cls : 'btn-group roo-upload-cropbox-trash',
32311 cls : 'btn btn-default',
32312 html : '<i class="fa fa-trash"></i>'
32318 cls : 'btn-group roo-upload-cropbox-rotate-right',
32319 action : 'rotate-right',
32323 cls : 'btn btn-default',
32324 html : '<i class="fa fa-repeat"></i>'
32332 cls : 'btn-group roo-upload-cropbox-rotate-left',
32333 action : 'rotate-left',
32337 cls : 'btn btn-default',
32338 html : '<i class="fa fa-undo"></i>'
32344 cls : 'btn-group roo-upload-cropbox-rotate-right',
32345 action : 'rotate-right',
32349 cls : 'btn btn-default',
32350 html : '<i class="fa fa-repeat"></i>'
32363 * @class Roo.bootstrap.DocumentManager
32364 * @extends Roo.bootstrap.Component
32365 * Bootstrap DocumentManager class
32366 * @cfg {String} paramName default 'imageUpload'
32367 * @cfg {String} toolTipName default 'filename'
32368 * @cfg {String} method default POST
32369 * @cfg {String} url action url
32370 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32371 * @cfg {Boolean} multiple multiple upload default true
32372 * @cfg {Number} thumbSize default 300
32373 * @cfg {String} fieldLabel
32374 * @cfg {Number} labelWidth default 4
32375 * @cfg {String} labelAlign (left|top) default left
32376 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32377 * @cfg {Number} labellg set the width of label (1-12)
32378 * @cfg {Number} labelmd set the width of label (1-12)
32379 * @cfg {Number} labelsm set the width of label (1-12)
32380 * @cfg {Number} labelxs set the width of label (1-12)
32383 * Create a new DocumentManager
32384 * @param {Object} config The config object
32387 Roo.bootstrap.DocumentManager = function(config){
32388 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32391 this.delegates = [];
32396 * Fire when initial the DocumentManager
32397 * @param {Roo.bootstrap.DocumentManager} this
32402 * inspect selected file
32403 * @param {Roo.bootstrap.DocumentManager} this
32404 * @param {File} file
32409 * Fire when xhr load exception
32410 * @param {Roo.bootstrap.DocumentManager} this
32411 * @param {XMLHttpRequest} xhr
32413 "exception" : true,
32415 * @event afterupload
32416 * Fire when xhr load exception
32417 * @param {Roo.bootstrap.DocumentManager} this
32418 * @param {XMLHttpRequest} xhr
32420 "afterupload" : true,
32423 * prepare the form data
32424 * @param {Roo.bootstrap.DocumentManager} this
32425 * @param {Object} formData
32430 * Fire when remove the file
32431 * @param {Roo.bootstrap.DocumentManager} this
32432 * @param {Object} file
32437 * Fire after refresh the file
32438 * @param {Roo.bootstrap.DocumentManager} this
32443 * Fire after click the image
32444 * @param {Roo.bootstrap.DocumentManager} this
32445 * @param {Object} file
32450 * Fire when upload a image and editable set to true
32451 * @param {Roo.bootstrap.DocumentManager} this
32452 * @param {Object} file
32456 * @event beforeselectfile
32457 * Fire before select file
32458 * @param {Roo.bootstrap.DocumentManager} this
32460 "beforeselectfile" : true,
32463 * Fire before process file
32464 * @param {Roo.bootstrap.DocumentManager} this
32465 * @param {Object} file
32469 * @event previewrendered
32470 * Fire when preview rendered
32471 * @param {Roo.bootstrap.DocumentManager} this
32472 * @param {Object} file
32474 "previewrendered" : true,
32477 "previewResize" : true
32482 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32491 paramName : 'imageUpload',
32492 toolTipName : 'filename',
32495 labelAlign : 'left',
32505 getAutoCreate : function()
32507 var managerWidget = {
32509 cls : 'roo-document-manager',
32513 cls : 'roo-document-manager-selector',
32518 cls : 'roo-document-manager-uploader',
32522 cls : 'roo-document-manager-upload-btn',
32523 html : '<i class="fa fa-plus"></i>'
32534 cls : 'column col-md-12',
32539 if(this.fieldLabel.length){
32544 cls : 'column col-md-12',
32545 html : this.fieldLabel
32549 cls : 'column col-md-12',
32554 if(this.labelAlign == 'left'){
32559 html : this.fieldLabel
32568 if(this.labelWidth > 12){
32569 content[0].style = "width: " + this.labelWidth + 'px';
32572 if(this.labelWidth < 13 && this.labelmd == 0){
32573 this.labelmd = this.labelWidth;
32576 if(this.labellg > 0){
32577 content[0].cls += ' col-lg-' + this.labellg;
32578 content[1].cls += ' col-lg-' + (12 - this.labellg);
32581 if(this.labelmd > 0){
32582 content[0].cls += ' col-md-' + this.labelmd;
32583 content[1].cls += ' col-md-' + (12 - this.labelmd);
32586 if(this.labelsm > 0){
32587 content[0].cls += ' col-sm-' + this.labelsm;
32588 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32591 if(this.labelxs > 0){
32592 content[0].cls += ' col-xs-' + this.labelxs;
32593 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32601 cls : 'row clearfix',
32609 initEvents : function()
32611 this.managerEl = this.el.select('.roo-document-manager', true).first();
32612 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32614 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32615 this.selectorEl.hide();
32618 this.selectorEl.attr('multiple', 'multiple');
32621 this.selectorEl.on('change', this.onFileSelected, this);
32623 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32624 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32626 this.uploader.on('click', this.onUploaderClick, this);
32628 this.renderProgressDialog();
32632 window.addEventListener("resize", function() { _this.refresh(); } );
32634 this.fireEvent('initial', this);
32637 renderProgressDialog : function()
32641 this.progressDialog = new Roo.bootstrap.Modal({
32642 cls : 'roo-document-manager-progress-dialog',
32643 allow_close : false,
32654 btnclick : function() {
32655 _this.uploadCancel();
32661 this.progressDialog.render(Roo.get(document.body));
32663 this.progress = new Roo.bootstrap.Progress({
32664 cls : 'roo-document-manager-progress',
32669 this.progress.render(this.progressDialog.getChildContainer());
32671 this.progressBar = new Roo.bootstrap.ProgressBar({
32672 cls : 'roo-document-manager-progress-bar',
32675 aria_valuemax : 12,
32679 this.progressBar.render(this.progress.getChildContainer());
32682 onUploaderClick : function(e)
32684 e.preventDefault();
32686 if(this.fireEvent('beforeselectfile', this) != false){
32687 this.selectorEl.dom.click();
32692 onFileSelected : function(e)
32694 e.preventDefault();
32696 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32700 Roo.each(this.selectorEl.dom.files, function(file){
32701 if(this.fireEvent('inspect', this, file) != false){
32702 this.files.push(file);
32712 this.selectorEl.dom.value = '';
32714 if(!this.files || !this.files.length){
32718 if(this.boxes > 0 && this.files.length > this.boxes){
32719 this.files = this.files.slice(0, this.boxes);
32722 this.uploader.show();
32724 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32725 this.uploader.hide();
32734 Roo.each(this.files, function(file){
32736 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32737 var f = this.renderPreview(file);
32742 if(file.type.indexOf('image') != -1){
32743 this.delegates.push(
32745 _this.process(file);
32746 }).createDelegate(this)
32754 _this.process(file);
32755 }).createDelegate(this)
32760 this.files = files;
32762 this.delegates = this.delegates.concat(docs);
32764 if(!this.delegates.length){
32769 this.progressBar.aria_valuemax = this.delegates.length;
32776 arrange : function()
32778 if(!this.delegates.length){
32779 this.progressDialog.hide();
32784 var delegate = this.delegates.shift();
32786 this.progressDialog.show();
32788 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32790 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32795 refresh : function()
32797 this.uploader.show();
32799 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32800 this.uploader.hide();
32803 Roo.isTouch ? this.closable(false) : this.closable(true);
32805 this.fireEvent('refresh', this);
32808 onRemove : function(e, el, o)
32810 e.preventDefault();
32812 this.fireEvent('remove', this, o);
32816 remove : function(o)
32820 Roo.each(this.files, function(file){
32821 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32830 this.files = files;
32837 Roo.each(this.files, function(file){
32842 file.target.remove();
32851 onClick : function(e, el, o)
32853 e.preventDefault();
32855 this.fireEvent('click', this, o);
32859 closable : function(closable)
32861 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32863 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32875 xhrOnLoad : function(xhr)
32877 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32881 if (xhr.readyState !== 4) {
32883 this.fireEvent('exception', this, xhr);
32887 var response = Roo.decode(xhr.responseText);
32889 if(!response.success){
32891 this.fireEvent('exception', this, xhr);
32895 var file = this.renderPreview(response.data);
32897 this.files.push(file);
32901 this.fireEvent('afterupload', this, xhr);
32905 xhrOnError : function(xhr)
32907 Roo.log('xhr on error');
32909 var response = Roo.decode(xhr.responseText);
32916 process : function(file)
32918 if(this.fireEvent('process', this, file) !== false){
32919 if(this.editable && file.type.indexOf('image') != -1){
32920 this.fireEvent('edit', this, file);
32924 this.uploadStart(file, false);
32931 uploadStart : function(file, crop)
32933 this.xhr = new XMLHttpRequest();
32935 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32940 file.xhr = this.xhr;
32942 this.managerEl.createChild({
32944 cls : 'roo-document-manager-loading',
32948 tooltip : file.name,
32949 cls : 'roo-document-manager-thumb',
32950 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32956 this.xhr.open(this.method, this.url, true);
32959 "Accept": "application/json",
32960 "Cache-Control": "no-cache",
32961 "X-Requested-With": "XMLHttpRequest"
32964 for (var headerName in headers) {
32965 var headerValue = headers[headerName];
32967 this.xhr.setRequestHeader(headerName, headerValue);
32973 this.xhr.onload = function()
32975 _this.xhrOnLoad(_this.xhr);
32978 this.xhr.onerror = function()
32980 _this.xhrOnError(_this.xhr);
32983 var formData = new FormData();
32985 formData.append('returnHTML', 'NO');
32988 formData.append('crop', crop);
32991 formData.append(this.paramName, file, file.name);
32998 if(this.fireEvent('prepare', this, formData, options) != false){
33000 if(options.manually){
33004 this.xhr.send(formData);
33008 this.uploadCancel();
33011 uploadCancel : function()
33017 this.delegates = [];
33019 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33026 renderPreview : function(file)
33028 if(typeof(file.target) != 'undefined' && file.target){
33032 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33034 var previewEl = this.managerEl.createChild({
33036 cls : 'roo-document-manager-preview',
33040 tooltip : file[this.toolTipName],
33041 cls : 'roo-document-manager-thumb',
33042 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33047 html : '<i class="fa fa-times-circle"></i>'
33052 var close = previewEl.select('button.close', true).first();
33054 close.on('click', this.onRemove, this, file);
33056 file.target = previewEl;
33058 var image = previewEl.select('img', true).first();
33062 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33064 image.on('click', this.onClick, this, file);
33066 this.fireEvent('previewrendered', this, file);
33072 onPreviewLoad : function(file, image)
33074 if(typeof(file.target) == 'undefined' || !file.target){
33078 var width = image.dom.naturalWidth || image.dom.width;
33079 var height = image.dom.naturalHeight || image.dom.height;
33081 if(!this.previewResize) {
33085 if(width > height){
33086 file.target.addClass('wide');
33090 file.target.addClass('tall');
33095 uploadFromSource : function(file, crop)
33097 this.xhr = new XMLHttpRequest();
33099 this.managerEl.createChild({
33101 cls : 'roo-document-manager-loading',
33105 tooltip : file.name,
33106 cls : 'roo-document-manager-thumb',
33107 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33113 this.xhr.open(this.method, this.url, true);
33116 "Accept": "application/json",
33117 "Cache-Control": "no-cache",
33118 "X-Requested-With": "XMLHttpRequest"
33121 for (var headerName in headers) {
33122 var headerValue = headers[headerName];
33124 this.xhr.setRequestHeader(headerName, headerValue);
33130 this.xhr.onload = function()
33132 _this.xhrOnLoad(_this.xhr);
33135 this.xhr.onerror = function()
33137 _this.xhrOnError(_this.xhr);
33140 var formData = new FormData();
33142 formData.append('returnHTML', 'NO');
33144 formData.append('crop', crop);
33146 if(typeof(file.filename) != 'undefined'){
33147 formData.append('filename', file.filename);
33150 if(typeof(file.mimetype) != 'undefined'){
33151 formData.append('mimetype', file.mimetype);
33156 if(this.fireEvent('prepare', this, formData) != false){
33157 this.xhr.send(formData);
33167 * @class Roo.bootstrap.DocumentViewer
33168 * @extends Roo.bootstrap.Component
33169 * Bootstrap DocumentViewer class
33170 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33171 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33174 * Create a new DocumentViewer
33175 * @param {Object} config The config object
33178 Roo.bootstrap.DocumentViewer = function(config){
33179 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33184 * Fire after initEvent
33185 * @param {Roo.bootstrap.DocumentViewer} this
33191 * @param {Roo.bootstrap.DocumentViewer} this
33196 * Fire after download button
33197 * @param {Roo.bootstrap.DocumentViewer} this
33202 * Fire after trash button
33203 * @param {Roo.bootstrap.DocumentViewer} this
33210 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33212 showDownload : true,
33216 getAutoCreate : function()
33220 cls : 'roo-document-viewer',
33224 cls : 'roo-document-viewer-body',
33228 cls : 'roo-document-viewer-thumb',
33232 cls : 'roo-document-viewer-image'
33240 cls : 'roo-document-viewer-footer',
33243 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33247 cls : 'btn-group roo-document-viewer-download',
33251 cls : 'btn btn-default',
33252 html : '<i class="fa fa-download"></i>'
33258 cls : 'btn-group roo-document-viewer-trash',
33262 cls : 'btn btn-default',
33263 html : '<i class="fa fa-trash"></i>'
33276 initEvents : function()
33278 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33279 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33281 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33282 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33284 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33285 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33287 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33288 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33290 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33291 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33293 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33294 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33296 this.bodyEl.on('click', this.onClick, this);
33297 this.downloadBtn.on('click', this.onDownload, this);
33298 this.trashBtn.on('click', this.onTrash, this);
33300 this.downloadBtn.hide();
33301 this.trashBtn.hide();
33303 if(this.showDownload){
33304 this.downloadBtn.show();
33307 if(this.showTrash){
33308 this.trashBtn.show();
33311 if(!this.showDownload && !this.showTrash) {
33312 this.footerEl.hide();
33317 initial : function()
33319 this.fireEvent('initial', this);
33323 onClick : function(e)
33325 e.preventDefault();
33327 this.fireEvent('click', this);
33330 onDownload : function(e)
33332 e.preventDefault();
33334 this.fireEvent('download', this);
33337 onTrash : function(e)
33339 e.preventDefault();
33341 this.fireEvent('trash', this);
33353 * @class Roo.bootstrap.NavProgressBar
33354 * @extends Roo.bootstrap.Component
33355 * Bootstrap NavProgressBar class
33358 * Create a new nav progress bar - a bar indicating step along a process
33359 * @param {Object} config The config object
33362 Roo.bootstrap.NavProgressBar = function(config){
33363 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33365 this.bullets = this.bullets || [];
33367 // Roo.bootstrap.NavProgressBar.register(this);
33371 * Fires when the active item changes
33372 * @param {Roo.bootstrap.NavProgressBar} this
33373 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33374 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
33381 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
33383 * @cfg {Roo.bootstrap.NavProgressItem} NavProgressBar:bullets[]
33384 * Bullets for the Nav Progress bar for the toolbar
33389 getAutoCreate : function()
33391 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33395 cls : 'roo-navigation-bar-group',
33399 cls : 'roo-navigation-top-bar'
33403 cls : 'roo-navigation-bullets-bar',
33407 cls : 'roo-navigation-bar'
33414 cls : 'roo-navigation-bottom-bar'
33424 initEvents: function()
33429 onRender : function(ct, position)
33431 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33433 if(this.bullets.length){
33434 Roo.each(this.bullets, function(b){
33443 addItem : function(cfg)
33445 var item = new Roo.bootstrap.NavProgressItem(cfg);
33447 item.parentId = this.id;
33448 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33451 var top = new Roo.bootstrap.Element({
33453 cls : 'roo-navigation-bar-text'
33456 var bottom = new Roo.bootstrap.Element({
33458 cls : 'roo-navigation-bar-text'
33461 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33462 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33464 var topText = new Roo.bootstrap.Element({
33466 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33469 var bottomText = new Roo.bootstrap.Element({
33471 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33474 topText.onRender(top.el, null);
33475 bottomText.onRender(bottom.el, null);
33478 item.bottomEl = bottom;
33481 this.barItems.push(item);
33486 getActive : function()
33488 var active = false;
33490 Roo.each(this.barItems, function(v){
33492 if (!v.isActive()) {
33504 setActiveItem : function(item)
33508 Roo.each(this.barItems, function(v){
33509 if (v.rid == item.rid) {
33513 if (v.isActive()) {
33514 v.setActive(false);
33519 item.setActive(true);
33521 this.fireEvent('changed', this, item, prev);
33524 getBarItem: function(rid)
33528 Roo.each(this.barItems, function(e) {
33529 if (e.rid != rid) {
33540 indexOfItem : function(item)
33544 Roo.each(this.barItems, function(v, i){
33546 if (v.rid != item.rid) {
33557 setActiveNext : function()
33559 var i = this.indexOfItem(this.getActive());
33561 if (i > this.barItems.length) {
33565 this.setActiveItem(this.barItems[i+1]);
33568 setActivePrev : function()
33570 var i = this.indexOfItem(this.getActive());
33576 this.setActiveItem(this.barItems[i-1]);
33579 format : function()
33581 if(!this.barItems.length){
33585 var width = 100 / this.barItems.length;
33587 Roo.each(this.barItems, function(i){
33588 i.el.setStyle('width', width + '%');
33589 i.topEl.el.setStyle('width', width + '%');
33590 i.bottomEl.el.setStyle('width', width + '%');
33599 * Nav Progress Item
33604 * @class Roo.bootstrap.NavProgressItem
33605 * @extends Roo.bootstrap.Component
33606 * Bootstrap NavProgressItem class
33607 * @cfg {String} rid the reference id
33608 * @cfg {Boolean} active (true|false) Is item active default false
33609 * @cfg {Boolean} disabled (true|false) Is item active default false
33610 * @cfg {String} html
33611 * @cfg {String} position (top|bottom) text position default bottom
33612 * @cfg {String} icon show icon instead of number
33615 * Create a new NavProgressItem
33616 * @param {Object} config The config object
33618 Roo.bootstrap.NavProgressItem = function(config){
33619 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33624 * The raw click event for the entire grid.
33625 * @param {Roo.bootstrap.NavProgressItem} this
33626 * @param {Roo.EventObject} e
33633 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33639 position : 'bottom',
33642 getAutoCreate : function()
33644 var iconCls = 'roo-navigation-bar-item-icon';
33646 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33650 cls: 'roo-navigation-bar-item',
33660 cfg.cls += ' active';
33663 cfg.cls += ' disabled';
33669 disable : function()
33671 this.setDisabled(true);
33674 enable : function()
33676 this.setDisabled(false);
33679 initEvents: function()
33681 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33683 this.iconEl.on('click', this.onClick, this);
33686 onClick : function(e)
33688 e.preventDefault();
33694 if(this.fireEvent('click', this, e) === false){
33698 this.parent().setActiveItem(this);
33701 isActive: function ()
33703 return this.active;
33706 setActive : function(state)
33708 if(this.active == state){
33712 this.active = state;
33715 this.el.addClass('active');
33719 this.el.removeClass('active');
33724 setDisabled : function(state)
33726 if(this.disabled == state){
33730 this.disabled = state;
33733 this.el.addClass('disabled');
33737 this.el.removeClass('disabled');
33740 tooltipEl : function()
33742 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33755 * @class Roo.bootstrap.FieldLabel
33756 * @extends Roo.bootstrap.Component
33757 * Bootstrap FieldLabel class
33758 * @cfg {String} html contents of the element
33759 * @cfg {String} tag tag of the element default label
33760 * @cfg {String} cls class of the element
33761 * @cfg {String} target label target
33762 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33763 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33764 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33765 * @cfg {String} iconTooltip default "This field is required"
33766 * @cfg {String} indicatorpos (left|right) default left
33769 * Create a new FieldLabel
33770 * @param {Object} config The config object
33773 Roo.bootstrap.FieldLabel = function(config){
33774 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33779 * Fires after the field has been marked as invalid.
33780 * @param {Roo.form.FieldLabel} this
33781 * @param {String} msg The validation message
33786 * Fires after the field has been validated with no errors.
33787 * @param {Roo.form.FieldLabel} this
33793 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33800 invalidClass : 'has-warning',
33801 validClass : 'has-success',
33802 iconTooltip : 'This field is required',
33803 indicatorpos : 'left',
33805 getAutoCreate : function(){
33808 if (!this.allowBlank) {
33814 cls : 'roo-bootstrap-field-label ' + this.cls,
33819 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33820 tooltip : this.iconTooltip
33829 if(this.indicatorpos == 'right'){
33832 cls : 'roo-bootstrap-field-label ' + this.cls,
33841 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33842 tooltip : this.iconTooltip
33851 initEvents: function()
33853 Roo.bootstrap.Element.superclass.initEvents.call(this);
33855 this.indicator = this.indicatorEl();
33857 if(this.indicator){
33858 this.indicator.removeClass('visible');
33859 this.indicator.addClass('invisible');
33862 Roo.bootstrap.FieldLabel.register(this);
33865 indicatorEl : function()
33867 var indicator = this.el.select('i.roo-required-indicator',true).first();
33878 * Mark this field as valid
33880 markValid : function()
33882 if(this.indicator){
33883 this.indicator.removeClass('visible');
33884 this.indicator.addClass('invisible');
33886 if (Roo.bootstrap.version == 3) {
33887 this.el.removeClass(this.invalidClass);
33888 this.el.addClass(this.validClass);
33890 this.el.removeClass('is-invalid');
33891 this.el.addClass('is-valid');
33895 this.fireEvent('valid', this);
33899 * Mark this field as invalid
33900 * @param {String} msg The validation message
33902 markInvalid : function(msg)
33904 if(this.indicator){
33905 this.indicator.removeClass('invisible');
33906 this.indicator.addClass('visible');
33908 if (Roo.bootstrap.version == 3) {
33909 this.el.removeClass(this.validClass);
33910 this.el.addClass(this.invalidClass);
33912 this.el.removeClass('is-valid');
33913 this.el.addClass('is-invalid');
33917 this.fireEvent('invalid', this, msg);
33923 Roo.apply(Roo.bootstrap.FieldLabel, {
33928 * register a FieldLabel Group
33929 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33931 register : function(label)
33933 if(this.groups.hasOwnProperty(label.target)){
33937 this.groups[label.target] = label;
33941 * fetch a FieldLabel Group based on the target
33942 * @param {string} target
33943 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33945 get: function(target) {
33946 if (typeof(this.groups[target]) == 'undefined') {
33950 return this.groups[target] ;
33959 * page DateSplitField.
33965 * @class Roo.bootstrap.DateSplitField
33966 * @extends Roo.bootstrap.Component
33967 * Bootstrap DateSplitField class
33968 * @cfg {string} fieldLabel - the label associated
33969 * @cfg {Number} labelWidth set the width of label (0-12)
33970 * @cfg {String} labelAlign (top|left)
33971 * @cfg {Boolean} dayAllowBlank (true|false) default false
33972 * @cfg {Boolean} monthAllowBlank (true|false) default false
33973 * @cfg {Boolean} yearAllowBlank (true|false) default false
33974 * @cfg {string} dayPlaceholder
33975 * @cfg {string} monthPlaceholder
33976 * @cfg {string} yearPlaceholder
33977 * @cfg {string} dayFormat default 'd'
33978 * @cfg {string} monthFormat default 'm'
33979 * @cfg {string} yearFormat default 'Y'
33980 * @cfg {Number} labellg set the width of label (1-12)
33981 * @cfg {Number} labelmd set the width of label (1-12)
33982 * @cfg {Number} labelsm set the width of label (1-12)
33983 * @cfg {Number} labelxs set the width of label (1-12)
33987 * Create a new DateSplitField
33988 * @param {Object} config The config object
33991 Roo.bootstrap.DateSplitField = function(config){
33992 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33998 * getting the data of years
33999 * @param {Roo.bootstrap.DateSplitField} this
34000 * @param {Object} years
34005 * getting the data of days
34006 * @param {Roo.bootstrap.DateSplitField} this
34007 * @param {Object} days
34012 * Fires after the field has been marked as invalid.
34013 * @param {Roo.form.Field} this
34014 * @param {String} msg The validation message
34019 * Fires after the field has been validated with no errors.
34020 * @param {Roo.form.Field} this
34026 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
34029 labelAlign : 'top',
34031 dayAllowBlank : false,
34032 monthAllowBlank : false,
34033 yearAllowBlank : false,
34034 dayPlaceholder : '',
34035 monthPlaceholder : '',
34036 yearPlaceholder : '',
34040 isFormField : true,
34046 getAutoCreate : function()
34050 cls : 'row roo-date-split-field-group',
34055 cls : 'form-hidden-field roo-date-split-field-group-value',
34061 var labelCls = 'col-md-12';
34062 var contentCls = 'col-md-4';
34064 if(this.fieldLabel){
34068 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34072 html : this.fieldLabel
34077 if(this.labelAlign == 'left'){
34079 if(this.labelWidth > 12){
34080 label.style = "width: " + this.labelWidth + 'px';
34083 if(this.labelWidth < 13 && this.labelmd == 0){
34084 this.labelmd = this.labelWidth;
34087 if(this.labellg > 0){
34088 labelCls = ' col-lg-' + this.labellg;
34089 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34092 if(this.labelmd > 0){
34093 labelCls = ' col-md-' + this.labelmd;
34094 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34097 if(this.labelsm > 0){
34098 labelCls = ' col-sm-' + this.labelsm;
34099 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34102 if(this.labelxs > 0){
34103 labelCls = ' col-xs-' + this.labelxs;
34104 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34108 label.cls += ' ' + labelCls;
34110 cfg.cn.push(label);
34113 Roo.each(['day', 'month', 'year'], function(t){
34116 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34123 inputEl: function ()
34125 return this.el.select('.roo-date-split-field-group-value', true).first();
34128 onRender : function(ct, position)
34132 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34134 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34136 this.dayField = new Roo.bootstrap.ComboBox({
34137 allowBlank : this.dayAllowBlank,
34138 alwaysQuery : true,
34139 displayField : 'value',
34142 forceSelection : true,
34144 placeholder : this.dayPlaceholder,
34145 selectOnFocus : true,
34146 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34147 triggerAction : 'all',
34149 valueField : 'value',
34150 store : new Roo.data.SimpleStore({
34151 data : (function() {
34153 _this.fireEvent('days', _this, days);
34156 fields : [ 'value' ]
34159 select : function (_self, record, index)
34161 _this.setValue(_this.getValue());
34166 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34168 this.monthField = new Roo.bootstrap.MonthField({
34169 after : '<i class=\"fa fa-calendar\"></i>',
34170 allowBlank : this.monthAllowBlank,
34171 placeholder : this.monthPlaceholder,
34174 render : function (_self)
34176 this.el.select('span.input-group-addon', true).first().on('click', function(e){
34177 e.preventDefault();
34181 select : function (_self, oldvalue, newvalue)
34183 _this.setValue(_this.getValue());
34188 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34190 this.yearField = new Roo.bootstrap.ComboBox({
34191 allowBlank : this.yearAllowBlank,
34192 alwaysQuery : true,
34193 displayField : 'value',
34196 forceSelection : true,
34198 placeholder : this.yearPlaceholder,
34199 selectOnFocus : true,
34200 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34201 triggerAction : 'all',
34203 valueField : 'value',
34204 store : new Roo.data.SimpleStore({
34205 data : (function() {
34207 _this.fireEvent('years', _this, years);
34210 fields : [ 'value' ]
34213 select : function (_self, record, index)
34215 _this.setValue(_this.getValue());
34220 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34223 setValue : function(v, format)
34225 this.inputEl.dom.value = v;
34227 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34229 var d = Date.parseDate(v, f);
34236 this.setDay(d.format(this.dayFormat));
34237 this.setMonth(d.format(this.monthFormat));
34238 this.setYear(d.format(this.yearFormat));
34245 setDay : function(v)
34247 this.dayField.setValue(v);
34248 this.inputEl.dom.value = this.getValue();
34253 setMonth : function(v)
34255 this.monthField.setValue(v, true);
34256 this.inputEl.dom.value = this.getValue();
34261 setYear : function(v)
34263 this.yearField.setValue(v);
34264 this.inputEl.dom.value = this.getValue();
34269 getDay : function()
34271 return this.dayField.getValue();
34274 getMonth : function()
34276 return this.monthField.getValue();
34279 getYear : function()
34281 return this.yearField.getValue();
34284 getValue : function()
34286 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34288 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34298 this.inputEl.dom.value = '';
34303 validate : function()
34305 var d = this.dayField.validate();
34306 var m = this.monthField.validate();
34307 var y = this.yearField.validate();
34312 (!this.dayAllowBlank && !d) ||
34313 (!this.monthAllowBlank && !m) ||
34314 (!this.yearAllowBlank && !y)
34319 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34328 this.markInvalid();
34333 markValid : function()
34336 var label = this.el.select('label', true).first();
34337 var icon = this.el.select('i.fa-star', true).first();
34343 this.fireEvent('valid', this);
34347 * Mark this field as invalid
34348 * @param {String} msg The validation message
34350 markInvalid : function(msg)
34353 var label = this.el.select('label', true).first();
34354 var icon = this.el.select('i.fa-star', true).first();
34356 if(label && !icon){
34357 this.el.select('.roo-date-split-field-label', true).createChild({
34359 cls : 'text-danger fa fa-lg fa-star',
34360 tooltip : 'This field is required',
34361 style : 'margin-right:5px;'
34365 this.fireEvent('invalid', this, msg);
34368 clearInvalid : function()
34370 var label = this.el.select('label', true).first();
34371 var icon = this.el.select('i.fa-star', true).first();
34377 this.fireEvent('valid', this);
34380 getName: function()
34390 * http://masonry.desandro.com
34392 * The idea is to render all the bricks based on vertical width...
34394 * The original code extends 'outlayer' - we might need to use that....
34400 * @class Roo.bootstrap.LayoutMasonry
34401 * @extends Roo.bootstrap.Component
34402 * Bootstrap Layout Masonry class
34405 * Create a new Element
34406 * @param {Object} config The config object
34409 Roo.bootstrap.LayoutMasonry = function(config){
34411 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34415 Roo.bootstrap.LayoutMasonry.register(this);
34421 * Fire after layout the items
34422 * @param {Roo.bootstrap.LayoutMasonry} this
34423 * @param {Roo.EventObject} e
34430 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34433 * @cfg {Boolean} isLayoutInstant = no animation?
34435 isLayoutInstant : false, // needed?
34438 * @cfg {Number} boxWidth width of the columns
34443 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34448 * @cfg {Number} padWidth padding below box..
34453 * @cfg {Number} gutter gutter width..
34458 * @cfg {Number} maxCols maximum number of columns
34464 * @cfg {Boolean} isAutoInitial defalut true
34466 isAutoInitial : true,
34471 * @cfg {Boolean} isHorizontal defalut false
34473 isHorizontal : false,
34475 currentSize : null,
34481 bricks: null, //CompositeElement
34485 _isLayoutInited : false,
34487 // isAlternative : false, // only use for vertical layout...
34490 * @cfg {Number} alternativePadWidth padding below box..
34492 alternativePadWidth : 50,
34494 selectedBrick : [],
34496 getAutoCreate : function(){
34498 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34502 cls: 'blog-masonary-wrapper ' + this.cls,
34504 cls : 'mas-boxes masonary'
34511 getChildContainer: function( )
34513 if (this.boxesEl) {
34514 return this.boxesEl;
34517 this.boxesEl = this.el.select('.mas-boxes').first();
34519 return this.boxesEl;
34523 initEvents : function()
34527 if(this.isAutoInitial){
34528 Roo.log('hook children rendered');
34529 this.on('childrenrendered', function() {
34530 Roo.log('children rendered');
34536 initial : function()
34538 this.selectedBrick = [];
34540 this.currentSize = this.el.getBox(true);
34542 Roo.EventManager.onWindowResize(this.resize, this);
34544 if(!this.isAutoInitial){
34552 //this.layout.defer(500,this);
34556 resize : function()
34558 var cs = this.el.getBox(true);
34561 this.currentSize.width == cs.width &&
34562 this.currentSize.x == cs.x &&
34563 this.currentSize.height == cs.height &&
34564 this.currentSize.y == cs.y
34566 Roo.log("no change in with or X or Y");
34570 this.currentSize = cs;
34576 layout : function()
34578 this._resetLayout();
34580 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34582 this.layoutItems( isInstant );
34584 this._isLayoutInited = true;
34586 this.fireEvent('layout', this);
34590 _resetLayout : function()
34592 if(this.isHorizontal){
34593 this.horizontalMeasureColumns();
34597 this.verticalMeasureColumns();
34601 verticalMeasureColumns : function()
34603 this.getContainerWidth();
34605 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34606 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34610 var boxWidth = this.boxWidth + this.padWidth;
34612 if(this.containerWidth < this.boxWidth){
34613 boxWidth = this.containerWidth
34616 var containerWidth = this.containerWidth;
34618 var cols = Math.floor(containerWidth / boxWidth);
34620 this.cols = Math.max( cols, 1 );
34622 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34624 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34626 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34628 this.colWidth = boxWidth + avail - this.padWidth;
34630 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34631 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34634 horizontalMeasureColumns : function()
34636 this.getContainerWidth();
34638 var boxWidth = this.boxWidth;
34640 if(this.containerWidth < boxWidth){
34641 boxWidth = this.containerWidth;
34644 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34646 this.el.setHeight(boxWidth);
34650 getContainerWidth : function()
34652 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34655 layoutItems : function( isInstant )
34657 Roo.log(this.bricks);
34659 var items = Roo.apply([], this.bricks);
34661 if(this.isHorizontal){
34662 this._horizontalLayoutItems( items , isInstant );
34666 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34667 // this._verticalAlternativeLayoutItems( items , isInstant );
34671 this._verticalLayoutItems( items , isInstant );
34675 _verticalLayoutItems : function ( items , isInstant)
34677 if ( !items || !items.length ) {
34682 ['xs', 'xs', 'xs', 'tall'],
34683 ['xs', 'xs', 'tall'],
34684 ['xs', 'xs', 'sm'],
34685 ['xs', 'xs', 'xs'],
34691 ['sm', 'xs', 'xs'],
34695 ['tall', 'xs', 'xs', 'xs'],
34696 ['tall', 'xs', 'xs'],
34708 Roo.each(items, function(item, k){
34710 switch (item.size) {
34711 // these layouts take up a full box,
34722 boxes.push([item]);
34745 var filterPattern = function(box, length)
34753 var pattern = box.slice(0, length);
34757 Roo.each(pattern, function(i){
34758 format.push(i.size);
34761 Roo.each(standard, function(s){
34763 if(String(s) != String(format)){
34772 if(!match && length == 1){
34777 filterPattern(box, length - 1);
34781 queue.push(pattern);
34783 box = box.slice(length, box.length);
34785 filterPattern(box, 4);
34791 Roo.each(boxes, function(box, k){
34797 if(box.length == 1){
34802 filterPattern(box, 4);
34806 this._processVerticalLayoutQueue( queue, isInstant );
34810 // _verticalAlternativeLayoutItems : function( items , isInstant )
34812 // if ( !items || !items.length ) {
34816 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34820 _horizontalLayoutItems : function ( items , isInstant)
34822 if ( !items || !items.length || items.length < 3) {
34828 var eItems = items.slice(0, 3);
34830 items = items.slice(3, items.length);
34833 ['xs', 'xs', 'xs', 'wide'],
34834 ['xs', 'xs', 'wide'],
34835 ['xs', 'xs', 'sm'],
34836 ['xs', 'xs', 'xs'],
34842 ['sm', 'xs', 'xs'],
34846 ['wide', 'xs', 'xs', 'xs'],
34847 ['wide', 'xs', 'xs'],
34860 Roo.each(items, function(item, k){
34862 switch (item.size) {
34873 boxes.push([item]);
34897 var filterPattern = function(box, length)
34905 var pattern = box.slice(0, length);
34909 Roo.each(pattern, function(i){
34910 format.push(i.size);
34913 Roo.each(standard, function(s){
34915 if(String(s) != String(format)){
34924 if(!match && length == 1){
34929 filterPattern(box, length - 1);
34933 queue.push(pattern);
34935 box = box.slice(length, box.length);
34937 filterPattern(box, 4);
34943 Roo.each(boxes, function(box, k){
34949 if(box.length == 1){
34954 filterPattern(box, 4);
34961 var pos = this.el.getBox(true);
34965 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34967 var hit_end = false;
34969 Roo.each(queue, function(box){
34973 Roo.each(box, function(b){
34975 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34985 Roo.each(box, function(b){
34987 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34990 mx = Math.max(mx, b.x);
34994 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34998 Roo.each(box, function(b){
35000 b.el.setVisibilityMode(Roo.Element.DISPLAY);
35014 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
35017 /** Sets position of item in DOM
35018 * @param {Element} item
35019 * @param {Number} x - horizontal position
35020 * @param {Number} y - vertical position
35021 * @param {Boolean} isInstant - disables transitions
35023 _processVerticalLayoutQueue : function( queue, isInstant )
35025 var pos = this.el.getBox(true);
35030 for (var i = 0; i < this.cols; i++){
35034 Roo.each(queue, function(box, k){
35036 var col = k % this.cols;
35038 Roo.each(box, function(b,kk){
35040 b.el.position('absolute');
35042 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35043 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35045 if(b.size == 'md-left' || b.size == 'md-right'){
35046 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35047 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35050 b.el.setWidth(width);
35051 b.el.setHeight(height);
35053 b.el.select('iframe',true).setSize(width,height);
35057 for (var i = 0; i < this.cols; i++){
35059 if(maxY[i] < maxY[col]){
35064 col = Math.min(col, i);
35068 x = pos.x + col * (this.colWidth + this.padWidth);
35072 var positions = [];
35074 switch (box.length){
35076 positions = this.getVerticalOneBoxColPositions(x, y, box);
35079 positions = this.getVerticalTwoBoxColPositions(x, y, box);
35082 positions = this.getVerticalThreeBoxColPositions(x, y, box);
35085 positions = this.getVerticalFourBoxColPositions(x, y, box);
35091 Roo.each(box, function(b,kk){
35093 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35095 var sz = b.el.getSize();
35097 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35105 for (var i = 0; i < this.cols; i++){
35106 mY = Math.max(mY, maxY[i]);
35109 this.el.setHeight(mY - pos.y);
35113 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35115 // var pos = this.el.getBox(true);
35118 // var maxX = pos.right;
35120 // var maxHeight = 0;
35122 // Roo.each(items, function(item, k){
35126 // item.el.position('absolute');
35128 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35130 // item.el.setWidth(width);
35132 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35134 // item.el.setHeight(height);
35137 // item.el.setXY([x, y], isInstant ? false : true);
35139 // item.el.setXY([maxX - width, y], isInstant ? false : true);
35142 // y = y + height + this.alternativePadWidth;
35144 // maxHeight = maxHeight + height + this.alternativePadWidth;
35148 // this.el.setHeight(maxHeight);
35152 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35154 var pos = this.el.getBox(true);
35159 var maxX = pos.right;
35161 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35163 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35165 Roo.each(queue, function(box, k){
35167 Roo.each(box, function(b, kk){
35169 b.el.position('absolute');
35171 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35172 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35174 if(b.size == 'md-left' || b.size == 'md-right'){
35175 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35176 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35179 b.el.setWidth(width);
35180 b.el.setHeight(height);
35188 var positions = [];
35190 switch (box.length){
35192 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35195 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35198 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35201 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35207 Roo.each(box, function(b,kk){
35209 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35211 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35219 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35221 Roo.each(eItems, function(b,k){
35223 b.size = (k == 0) ? 'sm' : 'xs';
35224 b.x = (k == 0) ? 2 : 1;
35225 b.y = (k == 0) ? 2 : 1;
35227 b.el.position('absolute');
35229 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35231 b.el.setWidth(width);
35233 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35235 b.el.setHeight(height);
35239 var positions = [];
35242 x : maxX - this.unitWidth * 2 - this.gutter,
35247 x : maxX - this.unitWidth,
35248 y : minY + (this.unitWidth + this.gutter) * 2
35252 x : maxX - this.unitWidth * 3 - this.gutter * 2,
35256 Roo.each(eItems, function(b,k){
35258 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35264 getVerticalOneBoxColPositions : function(x, y, box)
35268 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35270 if(box[0].size == 'md-left'){
35274 if(box[0].size == 'md-right'){
35279 x : x + (this.unitWidth + this.gutter) * rand,
35286 getVerticalTwoBoxColPositions : function(x, y, box)
35290 if(box[0].size == 'xs'){
35294 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35298 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35312 x : x + (this.unitWidth + this.gutter) * 2,
35313 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35320 getVerticalThreeBoxColPositions : function(x, y, box)
35324 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35332 x : x + (this.unitWidth + this.gutter) * 1,
35337 x : x + (this.unitWidth + this.gutter) * 2,
35345 if(box[0].size == 'xs' && box[1].size == 'xs'){
35354 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35358 x : x + (this.unitWidth + this.gutter) * 1,
35372 x : x + (this.unitWidth + this.gutter) * 2,
35377 x : x + (this.unitWidth + this.gutter) * 2,
35378 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35385 getVerticalFourBoxColPositions : function(x, y, box)
35389 if(box[0].size == 'xs'){
35398 y : y + (this.unitHeight + this.gutter) * 1
35403 y : y + (this.unitHeight + this.gutter) * 2
35407 x : x + (this.unitWidth + this.gutter) * 1,
35421 x : x + (this.unitWidth + this.gutter) * 2,
35426 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35427 y : y + (this.unitHeight + this.gutter) * 1
35431 x : x + (this.unitWidth + this.gutter) * 2,
35432 y : y + (this.unitWidth + this.gutter) * 2
35439 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35443 if(box[0].size == 'md-left'){
35445 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35452 if(box[0].size == 'md-right'){
35454 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35455 y : minY + (this.unitWidth + this.gutter) * 1
35461 var rand = Math.floor(Math.random() * (4 - box[0].y));
35464 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35465 y : minY + (this.unitWidth + this.gutter) * rand
35472 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35476 if(box[0].size == 'xs'){
35479 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35484 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35485 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35493 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35498 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35499 y : minY + (this.unitWidth + this.gutter) * 2
35506 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35510 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35513 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35518 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35519 y : minY + (this.unitWidth + this.gutter) * 1
35523 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35524 y : minY + (this.unitWidth + this.gutter) * 2
35531 if(box[0].size == 'xs' && box[1].size == 'xs'){
35534 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35539 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35544 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35545 y : minY + (this.unitWidth + this.gutter) * 1
35553 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35558 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35559 y : minY + (this.unitWidth + this.gutter) * 2
35563 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35564 y : minY + (this.unitWidth + this.gutter) * 2
35571 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35575 if(box[0].size == 'xs'){
35578 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35583 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35588 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),
35593 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35594 y : minY + (this.unitWidth + this.gutter) * 1
35602 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35607 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35608 y : minY + (this.unitWidth + this.gutter) * 2
35612 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35613 y : minY + (this.unitWidth + this.gutter) * 2
35617 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),
35618 y : minY + (this.unitWidth + this.gutter) * 2
35626 * remove a Masonry Brick
35627 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35629 removeBrick : function(brick_id)
35635 for (var i = 0; i<this.bricks.length; i++) {
35636 if (this.bricks[i].id == brick_id) {
35637 this.bricks.splice(i,1);
35638 this.el.dom.removeChild(Roo.get(brick_id).dom);
35645 * adds a Masonry Brick
35646 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35648 addBrick : function(cfg)
35650 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35651 //this.register(cn);
35652 cn.parentId = this.id;
35653 cn.render(this.el);
35658 * register a Masonry Brick
35659 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35662 register : function(brick)
35664 this.bricks.push(brick);
35665 brick.masonryId = this.id;
35669 * clear all the Masonry Brick
35671 clearAll : function()
35674 //this.getChildContainer().dom.innerHTML = "";
35675 this.el.dom.innerHTML = '';
35678 getSelected : function()
35680 if (!this.selectedBrick) {
35684 return this.selectedBrick;
35688 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35692 * register a Masonry Layout
35693 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35696 register : function(layout)
35698 this.groups[layout.id] = layout;
35701 * fetch a Masonry Layout based on the masonry layout ID
35702 * @param {string} the masonry layout to add
35703 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35706 get: function(layout_id) {
35707 if (typeof(this.groups[layout_id]) == 'undefined') {
35710 return this.groups[layout_id] ;
35722 * http://masonry.desandro.com
35724 * The idea is to render all the bricks based on vertical width...
35726 * The original code extends 'outlayer' - we might need to use that....
35732 * @class Roo.bootstrap.LayoutMasonryAuto
35733 * @extends Roo.bootstrap.Component
35734 * Bootstrap Layout Masonry class
35737 * Create a new Element
35738 * @param {Object} config The config object
35741 Roo.bootstrap.LayoutMasonryAuto = function(config){
35742 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35745 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35748 * @cfg {Boolean} isFitWidth - resize the width..
35750 isFitWidth : false, // options..
35752 * @cfg {Boolean} isOriginLeft = left align?
35754 isOriginLeft : true,
35756 * @cfg {Boolean} isOriginTop = top align?
35758 isOriginTop : false,
35760 * @cfg {Boolean} isLayoutInstant = no animation?
35762 isLayoutInstant : false, // needed?
35764 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35766 isResizingContainer : true,
35768 * @cfg {Number} columnWidth width of the columns
35774 * @cfg {Number} maxCols maximum number of columns
35779 * @cfg {Number} padHeight padding below box..
35785 * @cfg {Boolean} isAutoInitial defalut true
35788 isAutoInitial : true,
35794 initialColumnWidth : 0,
35795 currentSize : null,
35797 colYs : null, // array.
35804 bricks: null, //CompositeElement
35805 cols : 0, // array?
35806 // element : null, // wrapped now this.el
35807 _isLayoutInited : null,
35810 getAutoCreate : function(){
35814 cls: 'blog-masonary-wrapper ' + this.cls,
35816 cls : 'mas-boxes masonary'
35823 getChildContainer: function( )
35825 if (this.boxesEl) {
35826 return this.boxesEl;
35829 this.boxesEl = this.el.select('.mas-boxes').first();
35831 return this.boxesEl;
35835 initEvents : function()
35839 if(this.isAutoInitial){
35840 Roo.log('hook children rendered');
35841 this.on('childrenrendered', function() {
35842 Roo.log('children rendered');
35849 initial : function()
35851 this.reloadItems();
35853 this.currentSize = this.el.getBox(true);
35855 /// was window resize... - let's see if this works..
35856 Roo.EventManager.onWindowResize(this.resize, this);
35858 if(!this.isAutoInitial){
35863 this.layout.defer(500,this);
35866 reloadItems: function()
35868 this.bricks = this.el.select('.masonry-brick', true);
35870 this.bricks.each(function(b) {
35871 //Roo.log(b.getSize());
35872 if (!b.attr('originalwidth')) {
35873 b.attr('originalwidth', b.getSize().width);
35878 Roo.log(this.bricks.elements.length);
35881 resize : function()
35884 var cs = this.el.getBox(true);
35886 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35887 Roo.log("no change in with or X");
35890 this.currentSize = cs;
35894 layout : function()
35897 this._resetLayout();
35898 //this._manageStamps();
35900 // don't animate first layout
35901 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35902 this.layoutItems( isInstant );
35904 // flag for initalized
35905 this._isLayoutInited = true;
35908 layoutItems : function( isInstant )
35910 //var items = this._getItemsForLayout( this.items );
35911 // original code supports filtering layout items.. we just ignore it..
35913 this._layoutItems( this.bricks , isInstant );
35915 this._postLayout();
35917 _layoutItems : function ( items , isInstant)
35919 //this.fireEvent( 'layout', this, items );
35922 if ( !items || !items.elements.length ) {
35923 // no items, emit event with empty array
35928 items.each(function(item) {
35929 Roo.log("layout item");
35931 // get x/y object from method
35932 var position = this._getItemLayoutPosition( item );
35934 position.item = item;
35935 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35936 queue.push( position );
35939 this._processLayoutQueue( queue );
35941 /** Sets position of item in DOM
35942 * @param {Element} item
35943 * @param {Number} x - horizontal position
35944 * @param {Number} y - vertical position
35945 * @param {Boolean} isInstant - disables transitions
35947 _processLayoutQueue : function( queue )
35949 for ( var i=0, len = queue.length; i < len; i++ ) {
35950 var obj = queue[i];
35951 obj.item.position('absolute');
35952 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35958 * Any logic you want to do after each layout,
35959 * i.e. size the container
35961 _postLayout : function()
35963 this.resizeContainer();
35966 resizeContainer : function()
35968 if ( !this.isResizingContainer ) {
35971 var size = this._getContainerSize();
35973 this.el.setSize(size.width,size.height);
35974 this.boxesEl.setSize(size.width,size.height);
35980 _resetLayout : function()
35982 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35983 this.colWidth = this.el.getWidth();
35984 //this.gutter = this.el.getWidth();
35986 this.measureColumns();
35992 this.colYs.push( 0 );
35998 measureColumns : function()
36000 this.getContainerWidth();
36001 // if columnWidth is 0, default to outerWidth of first item
36002 if ( !this.columnWidth ) {
36003 var firstItem = this.bricks.first();
36004 Roo.log(firstItem);
36005 this.columnWidth = this.containerWidth;
36006 if (firstItem && firstItem.attr('originalwidth') ) {
36007 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
36009 // columnWidth fall back to item of first element
36010 Roo.log("set column width?");
36011 this.initialColumnWidth = this.columnWidth ;
36013 // if first elem has no width, default to size of container
36018 if (this.initialColumnWidth) {
36019 this.columnWidth = this.initialColumnWidth;
36024 // column width is fixed at the top - however if container width get's smaller we should
36027 // this bit calcs how man columns..
36029 var columnWidth = this.columnWidth += this.gutter;
36031 // calculate columns
36032 var containerWidth = this.containerWidth + this.gutter;
36034 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
36035 // fix rounding errors, typically with gutters
36036 var excess = columnWidth - containerWidth % columnWidth;
36039 // if overshoot is less than a pixel, round up, otherwise floor it
36040 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
36041 cols = Math[ mathMethod ]( cols );
36042 this.cols = Math.max( cols, 1 );
36043 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36045 // padding positioning..
36046 var totalColWidth = this.cols * this.columnWidth;
36047 var padavail = this.containerWidth - totalColWidth;
36048 // so for 2 columns - we need 3 'pads'
36050 var padNeeded = (1+this.cols) * this.padWidth;
36052 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36054 this.columnWidth += padExtra
36055 //this.padWidth = Math.floor(padavail / ( this.cols));
36057 // adjust colum width so that padding is fixed??
36059 // we have 3 columns ... total = width * 3
36060 // we have X left over... that should be used by
36062 //if (this.expandC) {
36070 getContainerWidth : function()
36072 /* // container is parent if fit width
36073 var container = this.isFitWidth ? this.element.parentNode : this.element;
36074 // check that this.size and size are there
36075 // IE8 triggers resize on body size change, so they might not be
36077 var size = getSize( container ); //FIXME
36078 this.containerWidth = size && size.innerWidth; //FIXME
36081 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
36085 _getItemLayoutPosition : function( item ) // what is item?
36087 // we resize the item to our columnWidth..
36089 item.setWidth(this.columnWidth);
36090 item.autoBoxAdjust = false;
36092 var sz = item.getSize();
36094 // how many columns does this brick span
36095 var remainder = this.containerWidth % this.columnWidth;
36097 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36098 // round if off by 1 pixel, otherwise use ceil
36099 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
36100 colSpan = Math.min( colSpan, this.cols );
36102 // normally this should be '1' as we dont' currently allow multi width columns..
36104 var colGroup = this._getColGroup( colSpan );
36105 // get the minimum Y value from the columns
36106 var minimumY = Math.min.apply( Math, colGroup );
36107 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36109 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
36111 // position the brick
36113 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36114 y: this.currentSize.y + minimumY + this.padHeight
36118 // apply setHeight to necessary columns
36119 var setHeight = minimumY + sz.height + this.padHeight;
36120 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36122 var setSpan = this.cols + 1 - colGroup.length;
36123 for ( var i = 0; i < setSpan; i++ ) {
36124 this.colYs[ shortColIndex + i ] = setHeight ;
36131 * @param {Number} colSpan - number of columns the element spans
36132 * @returns {Array} colGroup
36134 _getColGroup : function( colSpan )
36136 if ( colSpan < 2 ) {
36137 // if brick spans only one column, use all the column Ys
36142 // how many different places could this brick fit horizontally
36143 var groupCount = this.cols + 1 - colSpan;
36144 // for each group potential horizontal position
36145 for ( var i = 0; i < groupCount; i++ ) {
36146 // make an array of colY values for that one group
36147 var groupColYs = this.colYs.slice( i, i + colSpan );
36148 // and get the max value of the array
36149 colGroup[i] = Math.max.apply( Math, groupColYs );
36154 _manageStamp : function( stamp )
36156 var stampSize = stamp.getSize();
36157 var offset = stamp.getBox();
36158 // get the columns that this stamp affects
36159 var firstX = this.isOriginLeft ? offset.x : offset.right;
36160 var lastX = firstX + stampSize.width;
36161 var firstCol = Math.floor( firstX / this.columnWidth );
36162 firstCol = Math.max( 0, firstCol );
36164 var lastCol = Math.floor( lastX / this.columnWidth );
36165 // lastCol should not go over if multiple of columnWidth #425
36166 lastCol -= lastX % this.columnWidth ? 0 : 1;
36167 lastCol = Math.min( this.cols - 1, lastCol );
36169 // set colYs to bottom of the stamp
36170 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36173 for ( var i = firstCol; i <= lastCol; i++ ) {
36174 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36179 _getContainerSize : function()
36181 this.maxY = Math.max.apply( Math, this.colYs );
36186 if ( this.isFitWidth ) {
36187 size.width = this._getContainerFitWidth();
36193 _getContainerFitWidth : function()
36195 var unusedCols = 0;
36196 // count unused columns
36199 if ( this.colYs[i] !== 0 ) {
36204 // fit container to columns that have been used
36205 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36208 needsResizeLayout : function()
36210 var previousWidth = this.containerWidth;
36211 this.getContainerWidth();
36212 return previousWidth !== this.containerWidth;
36227 * @class Roo.bootstrap.MasonryBrick
36228 * @extends Roo.bootstrap.Component
36229 * Bootstrap MasonryBrick class
36232 * Create a new MasonryBrick
36233 * @param {Object} config The config object
36236 Roo.bootstrap.MasonryBrick = function(config){
36238 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36240 Roo.bootstrap.MasonryBrick.register(this);
36246 * When a MasonryBrick is clcik
36247 * @param {Roo.bootstrap.MasonryBrick} this
36248 * @param {Roo.EventObject} e
36254 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
36257 * @cfg {String} title
36261 * @cfg {String} html
36265 * @cfg {String} bgimage
36269 * @cfg {String} videourl
36273 * @cfg {String} cls
36277 * @cfg {String} href
36281 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36286 * @cfg {String} placetitle (center|bottom)
36291 * @cfg {Boolean} isFitContainer defalut true
36293 isFitContainer : true,
36296 * @cfg {Boolean} preventDefault defalut false
36298 preventDefault : false,
36301 * @cfg {Boolean} inverse defalut false
36303 maskInverse : false,
36305 getAutoCreate : function()
36307 if(!this.isFitContainer){
36308 return this.getSplitAutoCreate();
36311 var cls = 'masonry-brick masonry-brick-full';
36313 if(this.href.length){
36314 cls += ' masonry-brick-link';
36317 if(this.bgimage.length){
36318 cls += ' masonry-brick-image';
36321 if(this.maskInverse){
36322 cls += ' mask-inverse';
36325 if(!this.html.length && !this.maskInverse && !this.videourl.length){
36326 cls += ' enable-mask';
36330 cls += ' masonry-' + this.size + '-brick';
36333 if(this.placetitle.length){
36335 switch (this.placetitle) {
36337 cls += ' masonry-center-title';
36340 cls += ' masonry-bottom-title';
36347 if(!this.html.length && !this.bgimage.length){
36348 cls += ' masonry-center-title';
36351 if(!this.html.length && this.bgimage.length){
36352 cls += ' masonry-bottom-title';
36357 cls += ' ' + this.cls;
36361 tag: (this.href.length) ? 'a' : 'div',
36366 cls: 'masonry-brick-mask'
36370 cls: 'masonry-brick-paragraph',
36376 if(this.href.length){
36377 cfg.href = this.href;
36380 var cn = cfg.cn[1].cn;
36382 if(this.title.length){
36385 cls: 'masonry-brick-title',
36390 if(this.html.length){
36393 cls: 'masonry-brick-text',
36398 if (!this.title.length && !this.html.length) {
36399 cfg.cn[1].cls += ' hide';
36402 if(this.bgimage.length){
36405 cls: 'masonry-brick-image-view',
36410 if(this.videourl.length){
36411 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36412 // youtube support only?
36415 cls: 'masonry-brick-image-view',
36418 allowfullscreen : true
36426 getSplitAutoCreate : function()
36428 var cls = 'masonry-brick masonry-brick-split';
36430 if(this.href.length){
36431 cls += ' masonry-brick-link';
36434 if(this.bgimage.length){
36435 cls += ' masonry-brick-image';
36439 cls += ' masonry-' + this.size + '-brick';
36442 switch (this.placetitle) {
36444 cls += ' masonry-center-title';
36447 cls += ' masonry-bottom-title';
36450 if(!this.bgimage.length){
36451 cls += ' masonry-center-title';
36454 if(this.bgimage.length){
36455 cls += ' masonry-bottom-title';
36461 cls += ' ' + this.cls;
36465 tag: (this.href.length) ? 'a' : 'div',
36470 cls: 'masonry-brick-split-head',
36474 cls: 'masonry-brick-paragraph',
36481 cls: 'masonry-brick-split-body',
36487 if(this.href.length){
36488 cfg.href = this.href;
36491 if(this.title.length){
36492 cfg.cn[0].cn[0].cn.push({
36494 cls: 'masonry-brick-title',
36499 if(this.html.length){
36500 cfg.cn[1].cn.push({
36502 cls: 'masonry-brick-text',
36507 if(this.bgimage.length){
36508 cfg.cn[0].cn.push({
36510 cls: 'masonry-brick-image-view',
36515 if(this.videourl.length){
36516 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36517 // youtube support only?
36518 cfg.cn[0].cn.cn.push({
36520 cls: 'masonry-brick-image-view',
36523 allowfullscreen : true
36530 initEvents: function()
36532 switch (this.size) {
36565 this.el.on('touchstart', this.onTouchStart, this);
36566 this.el.on('touchmove', this.onTouchMove, this);
36567 this.el.on('touchend', this.onTouchEnd, this);
36568 this.el.on('contextmenu', this.onContextMenu, this);
36570 this.el.on('mouseenter' ,this.enter, this);
36571 this.el.on('mouseleave', this.leave, this);
36572 this.el.on('click', this.onClick, this);
36575 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36576 this.parent().bricks.push(this);
36581 onClick: function(e, el)
36583 var time = this.endTimer - this.startTimer;
36584 // Roo.log(e.preventDefault());
36587 e.preventDefault();
36592 if(!this.preventDefault){
36596 e.preventDefault();
36598 if (this.activeClass != '') {
36599 this.selectBrick();
36602 this.fireEvent('click', this, e);
36605 enter: function(e, el)
36607 e.preventDefault();
36609 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36613 if(this.bgimage.length && this.html.length){
36614 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36618 leave: function(e, el)
36620 e.preventDefault();
36622 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36626 if(this.bgimage.length && this.html.length){
36627 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36631 onTouchStart: function(e, el)
36633 // e.preventDefault();
36635 this.touchmoved = false;
36637 if(!this.isFitContainer){
36641 if(!this.bgimage.length || !this.html.length){
36645 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36647 this.timer = new Date().getTime();
36651 onTouchMove: function(e, el)
36653 this.touchmoved = true;
36656 onContextMenu : function(e,el)
36658 e.preventDefault();
36659 e.stopPropagation();
36663 onTouchEnd: function(e, el)
36665 // e.preventDefault();
36667 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36674 if(!this.bgimage.length || !this.html.length){
36676 if(this.href.length){
36677 window.location.href = this.href;
36683 if(!this.isFitContainer){
36687 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36689 window.location.href = this.href;
36692 //selection on single brick only
36693 selectBrick : function() {
36695 if (!this.parentId) {
36699 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36700 var index = m.selectedBrick.indexOf(this.id);
36703 m.selectedBrick.splice(index,1);
36704 this.el.removeClass(this.activeClass);
36708 for(var i = 0; i < m.selectedBrick.length; i++) {
36709 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36710 b.el.removeClass(b.activeClass);
36713 m.selectedBrick = [];
36715 m.selectedBrick.push(this.id);
36716 this.el.addClass(this.activeClass);
36720 isSelected : function(){
36721 return this.el.hasClass(this.activeClass);
36726 Roo.apply(Roo.bootstrap.MasonryBrick, {
36729 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36731 * register a Masonry Brick
36732 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36735 register : function(brick)
36737 //this.groups[brick.id] = brick;
36738 this.groups.add(brick.id, brick);
36741 * fetch a masonry brick based on the masonry brick ID
36742 * @param {string} the masonry brick to add
36743 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36746 get: function(brick_id)
36748 // if (typeof(this.groups[brick_id]) == 'undefined') {
36751 // return this.groups[brick_id] ;
36753 if(this.groups.key(brick_id)) {
36754 return this.groups.key(brick_id);
36772 * @class Roo.bootstrap.Brick
36773 * @extends Roo.bootstrap.Component
36774 * Bootstrap Brick class
36777 * Create a new Brick
36778 * @param {Object} config The config object
36781 Roo.bootstrap.Brick = function(config){
36782 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36788 * When a Brick is click
36789 * @param {Roo.bootstrap.Brick} this
36790 * @param {Roo.EventObject} e
36796 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36799 * @cfg {String} title
36803 * @cfg {String} html
36807 * @cfg {String} bgimage
36811 * @cfg {String} cls
36815 * @cfg {String} href
36819 * @cfg {String} video
36823 * @cfg {Boolean} square
36827 getAutoCreate : function()
36829 var cls = 'roo-brick';
36831 if(this.href.length){
36832 cls += ' roo-brick-link';
36835 if(this.bgimage.length){
36836 cls += ' roo-brick-image';
36839 if(!this.html.length && !this.bgimage.length){
36840 cls += ' roo-brick-center-title';
36843 if(!this.html.length && this.bgimage.length){
36844 cls += ' roo-brick-bottom-title';
36848 cls += ' ' + this.cls;
36852 tag: (this.href.length) ? 'a' : 'div',
36857 cls: 'roo-brick-paragraph',
36863 if(this.href.length){
36864 cfg.href = this.href;
36867 var cn = cfg.cn[0].cn;
36869 if(this.title.length){
36872 cls: 'roo-brick-title',
36877 if(this.html.length){
36880 cls: 'roo-brick-text',
36887 if(this.bgimage.length){
36890 cls: 'roo-brick-image-view',
36898 initEvents: function()
36900 if(this.title.length || this.html.length){
36901 this.el.on('mouseenter' ,this.enter, this);
36902 this.el.on('mouseleave', this.leave, this);
36905 Roo.EventManager.onWindowResize(this.resize, this);
36907 if(this.bgimage.length){
36908 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36909 this.imageEl.on('load', this.onImageLoad, this);
36916 onImageLoad : function()
36921 resize : function()
36923 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36925 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36927 if(this.bgimage.length){
36928 var image = this.el.select('.roo-brick-image-view', true).first();
36930 image.setWidth(paragraph.getWidth());
36933 image.setHeight(paragraph.getWidth());
36936 this.el.setHeight(image.getHeight());
36937 paragraph.setHeight(image.getHeight());
36943 enter: function(e, el)
36945 e.preventDefault();
36947 if(this.bgimage.length){
36948 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36949 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36953 leave: function(e, el)
36955 e.preventDefault();
36957 if(this.bgimage.length){
36958 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36959 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36974 * @class Roo.bootstrap.NumberField
36975 * @extends Roo.bootstrap.Input
36976 * Bootstrap NumberField class
36982 * Create a new NumberField
36983 * @param {Object} config The config object
36986 Roo.bootstrap.NumberField = function(config){
36987 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36990 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36993 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36995 allowDecimals : true,
36997 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36999 decimalSeparator : ".",
37001 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
37003 decimalPrecision : 2,
37005 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
37007 allowNegative : true,
37010 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
37014 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
37016 minValue : Number.NEGATIVE_INFINITY,
37018 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
37020 maxValue : Number.MAX_VALUE,
37022 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
37024 minText : "The minimum value for this field is {0}",
37026 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
37028 maxText : "The maximum value for this field is {0}",
37030 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
37031 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
37033 nanText : "{0} is not a valid number",
37035 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
37037 thousandsDelimiter : false,
37039 * @cfg {String} valueAlign alignment of value
37041 valueAlign : "left",
37043 getAutoCreate : function()
37045 var hiddenInput = {
37049 cls: 'hidden-number-input'
37053 hiddenInput.name = this.name;
37058 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37060 this.name = hiddenInput.name;
37062 if(cfg.cn.length > 0) {
37063 cfg.cn.push(hiddenInput);
37070 initEvents : function()
37072 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37074 var allowed = "0123456789";
37076 if(this.allowDecimals){
37077 allowed += this.decimalSeparator;
37080 if(this.allowNegative){
37084 if(this.thousandsDelimiter) {
37088 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37090 var keyPress = function(e){
37092 var k = e.getKey();
37094 var c = e.getCharCode();
37097 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37098 allowed.indexOf(String.fromCharCode(c)) === -1
37104 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37108 if(allowed.indexOf(String.fromCharCode(c)) === -1){
37113 this.el.on("keypress", keyPress, this);
37116 validateValue : function(value)
37119 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37123 var num = this.parseValue(value);
37126 this.markInvalid(String.format(this.nanText, value));
37130 if(num < this.minValue){
37131 this.markInvalid(String.format(this.minText, this.minValue));
37135 if(num > this.maxValue){
37136 this.markInvalid(String.format(this.maxText, this.maxValue));
37143 getValue : function()
37145 var v = this.hiddenEl().getValue();
37147 return this.fixPrecision(this.parseValue(v));
37150 parseValue : function(value)
37152 if(this.thousandsDelimiter) {
37154 r = new RegExp(",", "g");
37155 value = value.replace(r, "");
37158 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37159 return isNaN(value) ? '' : value;
37162 fixPrecision : function(value)
37164 if(this.thousandsDelimiter) {
37166 r = new RegExp(",", "g");
37167 value = value.replace(r, "");
37170 var nan = isNaN(value);
37172 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37173 return nan ? '' : value;
37175 return parseFloat(value).toFixed(this.decimalPrecision);
37178 setValue : function(v)
37180 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37186 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37188 this.inputEl().dom.value = (v == '') ? '' :
37189 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37191 if(!this.allowZero && v === '0') {
37192 this.hiddenEl().dom.value = '';
37193 this.inputEl().dom.value = '';
37200 decimalPrecisionFcn : function(v)
37202 return Math.floor(v);
37205 beforeBlur : function()
37207 var v = this.parseValue(this.getRawValue());
37209 if(v || v === 0 || v === ''){
37214 hiddenEl : function()
37216 return this.el.select('input.hidden-number-input',true).first();
37228 * @class Roo.bootstrap.DocumentSlider
37229 * @extends Roo.bootstrap.Component
37230 * Bootstrap DocumentSlider class
37233 * Create a new DocumentViewer
37234 * @param {Object} config The config object
37237 Roo.bootstrap.DocumentSlider = function(config){
37238 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37245 * Fire after initEvent
37246 * @param {Roo.bootstrap.DocumentSlider} this
37251 * Fire after update
37252 * @param {Roo.bootstrap.DocumentSlider} this
37258 * @param {Roo.bootstrap.DocumentSlider} this
37264 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
37270 getAutoCreate : function()
37274 cls : 'roo-document-slider',
37278 cls : 'roo-document-slider-header',
37282 cls : 'roo-document-slider-header-title'
37288 cls : 'roo-document-slider-body',
37292 cls : 'roo-document-slider-prev',
37296 cls : 'fa fa-chevron-left'
37302 cls : 'roo-document-slider-thumb',
37306 cls : 'roo-document-slider-image'
37312 cls : 'roo-document-slider-next',
37316 cls : 'fa fa-chevron-right'
37328 initEvents : function()
37330 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37331 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37333 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37334 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37336 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37337 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37339 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37340 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37342 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37343 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37345 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37346 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37348 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37349 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37351 this.thumbEl.on('click', this.onClick, this);
37353 this.prevIndicator.on('click', this.prev, this);
37355 this.nextIndicator.on('click', this.next, this);
37359 initial : function()
37361 if(this.files.length){
37362 this.indicator = 1;
37366 this.fireEvent('initial', this);
37369 update : function()
37371 this.imageEl.attr('src', this.files[this.indicator - 1]);
37373 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37375 this.prevIndicator.show();
37377 if(this.indicator == 1){
37378 this.prevIndicator.hide();
37381 this.nextIndicator.show();
37383 if(this.indicator == this.files.length){
37384 this.nextIndicator.hide();
37387 this.thumbEl.scrollTo('top');
37389 this.fireEvent('update', this);
37392 onClick : function(e)
37394 e.preventDefault();
37396 this.fireEvent('click', this);
37401 e.preventDefault();
37403 this.indicator = Math.max(1, this.indicator - 1);
37410 e.preventDefault();
37412 this.indicator = Math.min(this.files.length, this.indicator + 1);
37426 * @class Roo.bootstrap.RadioSet
37427 * @extends Roo.bootstrap.Input
37428 * @children Roo.bootstrap.Radio
37429 * Bootstrap RadioSet class
37430 * @cfg {String} indicatorpos (left|right) default left
37431 * @cfg {Boolean} inline (true|false) inline the element (default true)
37432 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37434 * Create a new RadioSet
37435 * @param {Object} config The config object
37438 Roo.bootstrap.RadioSet = function(config){
37440 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37444 Roo.bootstrap.RadioSet.register(this);
37449 * Fires when the element is checked or unchecked.
37450 * @param {Roo.bootstrap.RadioSet} this This radio
37451 * @param {Roo.bootstrap.Radio} item The checked item
37456 * Fires when the element is click.
37457 * @param {Roo.bootstrap.RadioSet} this This radio set
37458 * @param {Roo.bootstrap.Radio} item The checked item
37459 * @param {Roo.EventObject} e The event object
37466 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37474 indicatorpos : 'left',
37476 getAutoCreate : function()
37480 cls : 'roo-radio-set-label',
37484 html : this.fieldLabel
37488 if (Roo.bootstrap.version == 3) {
37491 if(this.indicatorpos == 'left'){
37494 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37495 tooltip : 'This field is required'
37500 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37501 tooltip : 'This field is required'
37507 cls : 'roo-radio-set-items'
37510 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37512 if (align === 'left' && this.fieldLabel.length) {
37515 cls : "roo-radio-set-right",
37521 if(this.labelWidth > 12){
37522 label.style = "width: " + this.labelWidth + 'px';
37525 if(this.labelWidth < 13 && this.labelmd == 0){
37526 this.labelmd = this.labelWidth;
37529 if(this.labellg > 0){
37530 label.cls += ' col-lg-' + this.labellg;
37531 items.cls += ' col-lg-' + (12 - this.labellg);
37534 if(this.labelmd > 0){
37535 label.cls += ' col-md-' + this.labelmd;
37536 items.cls += ' col-md-' + (12 - this.labelmd);
37539 if(this.labelsm > 0){
37540 label.cls += ' col-sm-' + this.labelsm;
37541 items.cls += ' col-sm-' + (12 - this.labelsm);
37544 if(this.labelxs > 0){
37545 label.cls += ' col-xs-' + this.labelxs;
37546 items.cls += ' col-xs-' + (12 - this.labelxs);
37552 cls : 'roo-radio-set',
37556 cls : 'roo-radio-set-input',
37559 value : this.value ? this.value : ''
37566 if(this.weight.length){
37567 cfg.cls += ' roo-radio-' + this.weight;
37571 cfg.cls += ' roo-radio-set-inline';
37575 ['xs','sm','md','lg'].map(function(size){
37576 if (settings[size]) {
37577 cfg.cls += ' col-' + size + '-' + settings[size];
37585 initEvents : function()
37587 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37588 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37590 if(!this.fieldLabel.length){
37591 this.labelEl.hide();
37594 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37595 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37597 this.indicator = this.indicatorEl();
37599 if(this.indicator){
37600 this.indicator.addClass('invisible');
37603 this.originalValue = this.getValue();
37607 inputEl: function ()
37609 return this.el.select('.roo-radio-set-input', true).first();
37612 getChildContainer : function()
37614 return this.itemsEl;
37617 register : function(item)
37619 this.radioes.push(item);
37623 validate : function()
37625 if(this.getVisibilityEl().hasClass('hidden')){
37631 Roo.each(this.radioes, function(i){
37640 if(this.allowBlank) {
37644 if(this.disabled || valid){
37649 this.markInvalid();
37654 markValid : function()
37656 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37657 this.indicatorEl().removeClass('visible');
37658 this.indicatorEl().addClass('invisible');
37662 if (Roo.bootstrap.version == 3) {
37663 this.el.removeClass([this.invalidClass, this.validClass]);
37664 this.el.addClass(this.validClass);
37666 this.el.removeClass(['is-invalid','is-valid']);
37667 this.el.addClass(['is-valid']);
37669 this.fireEvent('valid', this);
37672 markInvalid : function(msg)
37674 if(this.allowBlank || this.disabled){
37678 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37679 this.indicatorEl().removeClass('invisible');
37680 this.indicatorEl().addClass('visible');
37682 if (Roo.bootstrap.version == 3) {
37683 this.el.removeClass([this.invalidClass, this.validClass]);
37684 this.el.addClass(this.invalidClass);
37686 this.el.removeClass(['is-invalid','is-valid']);
37687 this.el.addClass(['is-invalid']);
37690 this.fireEvent('invalid', this, msg);
37694 setValue : function(v, suppressEvent)
37696 if(this.value === v){
37703 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37706 Roo.each(this.radioes, function(i){
37708 i.el.removeClass('checked');
37711 Roo.each(this.radioes, function(i){
37713 if(i.value === v || i.value.toString() === v.toString()){
37715 i.el.addClass('checked');
37717 if(suppressEvent !== true){
37718 this.fireEvent('check', this, i);
37729 clearInvalid : function(){
37731 if(!this.el || this.preventMark){
37735 this.el.removeClass([this.invalidClass]);
37737 this.fireEvent('valid', this);
37742 Roo.apply(Roo.bootstrap.RadioSet, {
37746 register : function(set)
37748 this.groups[set.name] = set;
37751 get: function(name)
37753 if (typeof(this.groups[name]) == 'undefined') {
37757 return this.groups[name] ;
37763 * Ext JS Library 1.1.1
37764 * Copyright(c) 2006-2007, Ext JS, LLC.
37766 * Originally Released Under LGPL - original licence link has changed is not relivant.
37769 * <script type="text/javascript">
37774 * @class Roo.bootstrap.SplitBar
37775 * @extends Roo.util.Observable
37776 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37780 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37781 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37782 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37783 split.minSize = 100;
37784 split.maxSize = 600;
37785 split.animate = true;
37786 split.on('moved', splitterMoved);
37789 * Create a new SplitBar
37790 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37791 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37792 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37793 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37794 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37795 position of the SplitBar).
37797 Roo.bootstrap.SplitBar = function(cfg){
37802 // dragElement : elm
37803 // resizingElement: el,
37805 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37806 // placement : Roo.bootstrap.SplitBar.LEFT ,
37807 // existingProxy ???
37810 this.el = Roo.get(cfg.dragElement, true);
37811 this.el.dom.unselectable = "on";
37813 this.resizingEl = Roo.get(cfg.resizingElement, true);
37817 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37818 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37821 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37824 * The minimum size of the resizing element. (Defaults to 0)
37830 * The maximum size of the resizing element. (Defaults to 2000)
37833 this.maxSize = 2000;
37836 * Whether to animate the transition to the new size
37839 this.animate = false;
37842 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37845 this.useShim = false;
37850 if(!cfg.existingProxy){
37852 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37854 this.proxy = Roo.get(cfg.existingProxy).dom;
37857 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37860 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37863 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37866 this.dragSpecs = {};
37869 * @private The adapter to use to positon and resize elements
37871 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37872 this.adapter.init(this);
37874 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37876 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37877 this.el.addClass("roo-splitbar-h");
37880 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37881 this.el.addClass("roo-splitbar-v");
37887 * Fires when the splitter is moved (alias for {@link #event-moved})
37888 * @param {Roo.bootstrap.SplitBar} this
37889 * @param {Number} newSize the new width or height
37894 * Fires when the splitter is moved
37895 * @param {Roo.bootstrap.SplitBar} this
37896 * @param {Number} newSize the new width or height
37900 * @event beforeresize
37901 * Fires before the splitter is dragged
37902 * @param {Roo.bootstrap.SplitBar} this
37904 "beforeresize" : true,
37906 "beforeapply" : true
37909 Roo.util.Observable.call(this);
37912 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37913 onStartProxyDrag : function(x, y){
37914 this.fireEvent("beforeresize", this);
37916 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37918 o.enableDisplayMode("block");
37919 // all splitbars share the same overlay
37920 Roo.bootstrap.SplitBar.prototype.overlay = o;
37922 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37923 this.overlay.show();
37924 Roo.get(this.proxy).setDisplayed("block");
37925 var size = this.adapter.getElementSize(this);
37926 this.activeMinSize = this.getMinimumSize();;
37927 this.activeMaxSize = this.getMaximumSize();;
37928 var c1 = size - this.activeMinSize;
37929 var c2 = Math.max(this.activeMaxSize - size, 0);
37930 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37931 this.dd.resetConstraints();
37932 this.dd.setXConstraint(
37933 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37934 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37936 this.dd.setYConstraint(0, 0);
37938 this.dd.resetConstraints();
37939 this.dd.setXConstraint(0, 0);
37940 this.dd.setYConstraint(
37941 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37942 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37945 this.dragSpecs.startSize = size;
37946 this.dragSpecs.startPoint = [x, y];
37947 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37951 * @private Called after the drag operation by the DDProxy
37953 onEndProxyDrag : function(e){
37954 Roo.get(this.proxy).setDisplayed(false);
37955 var endPoint = Roo.lib.Event.getXY(e);
37957 this.overlay.hide();
37960 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37961 newSize = this.dragSpecs.startSize +
37962 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37963 endPoint[0] - this.dragSpecs.startPoint[0] :
37964 this.dragSpecs.startPoint[0] - endPoint[0]
37967 newSize = this.dragSpecs.startSize +
37968 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37969 endPoint[1] - this.dragSpecs.startPoint[1] :
37970 this.dragSpecs.startPoint[1] - endPoint[1]
37973 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37974 if(newSize != this.dragSpecs.startSize){
37975 if(this.fireEvent('beforeapply', this, newSize) !== false){
37976 this.adapter.setElementSize(this, newSize);
37977 this.fireEvent("moved", this, newSize);
37978 this.fireEvent("resize", this, newSize);
37984 * Get the adapter this SplitBar uses
37985 * @return The adapter object
37987 getAdapter : function(){
37988 return this.adapter;
37992 * Set the adapter this SplitBar uses
37993 * @param {Object} adapter A SplitBar adapter object
37995 setAdapter : function(adapter){
37996 this.adapter = adapter;
37997 this.adapter.init(this);
38001 * Gets the minimum size for the resizing element
38002 * @return {Number} The minimum size
38004 getMinimumSize : function(){
38005 return this.minSize;
38009 * Sets the minimum size for the resizing element
38010 * @param {Number} minSize The minimum size
38012 setMinimumSize : function(minSize){
38013 this.minSize = minSize;
38017 * Gets the maximum size for the resizing element
38018 * @return {Number} The maximum size
38020 getMaximumSize : function(){
38021 return this.maxSize;
38025 * Sets the maximum size for the resizing element
38026 * @param {Number} maxSize The maximum size
38028 setMaximumSize : function(maxSize){
38029 this.maxSize = maxSize;
38033 * Sets the initialize size for the resizing element
38034 * @param {Number} size The initial size
38036 setCurrentSize : function(size){
38037 var oldAnimate = this.animate;
38038 this.animate = false;
38039 this.adapter.setElementSize(this, size);
38040 this.animate = oldAnimate;
38044 * Destroy this splitbar.
38045 * @param {Boolean} removeEl True to remove the element
38047 destroy : function(removeEl){
38049 this.shim.remove();
38052 this.proxy.parentNode.removeChild(this.proxy);
38060 * @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.
38062 Roo.bootstrap.SplitBar.createProxy = function(dir){
38063 var proxy = new Roo.Element(document.createElement("div"));
38064 proxy.unselectable();
38065 var cls = 'roo-splitbar-proxy';
38066 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38067 document.body.appendChild(proxy.dom);
38072 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38073 * Default Adapter. It assumes the splitter and resizing element are not positioned
38074 * elements and only gets/sets the width of the element. Generally used for table based layouts.
38076 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38079 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38080 // do nothing for now
38081 init : function(s){
38085 * Called before drag operations to get the current size of the resizing element.
38086 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38088 getElementSize : function(s){
38089 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38090 return s.resizingEl.getWidth();
38092 return s.resizingEl.getHeight();
38097 * Called after drag operations to set the size of the resizing element.
38098 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38099 * @param {Number} newSize The new size to set
38100 * @param {Function} onComplete A function to be invoked when resizing is complete
38102 setElementSize : function(s, newSize, onComplete){
38103 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38105 s.resizingEl.setWidth(newSize);
38107 onComplete(s, newSize);
38110 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38115 s.resizingEl.setHeight(newSize);
38117 onComplete(s, newSize);
38120 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38127 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38128 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38129 * Adapter that moves the splitter element to align with the resized sizing element.
38130 * Used with an absolute positioned SplitBar.
38131 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38132 * document.body, make sure you assign an id to the body element.
38134 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38135 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38136 this.container = Roo.get(container);
38139 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38140 init : function(s){
38141 this.basic.init(s);
38144 getElementSize : function(s){
38145 return this.basic.getElementSize(s);
38148 setElementSize : function(s, newSize, onComplete){
38149 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38152 moveSplitter : function(s){
38153 var yes = Roo.bootstrap.SplitBar;
38154 switch(s.placement){
38156 s.el.setX(s.resizingEl.getRight());
38159 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38162 s.el.setY(s.resizingEl.getBottom());
38165 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38172 * Orientation constant - Create a vertical SplitBar
38176 Roo.bootstrap.SplitBar.VERTICAL = 1;
38179 * Orientation constant - Create a horizontal SplitBar
38183 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38186 * Placement constant - The resizing element is to the left of the splitter element
38190 Roo.bootstrap.SplitBar.LEFT = 1;
38193 * Placement constant - The resizing element is to the right of the splitter element
38197 Roo.bootstrap.SplitBar.RIGHT = 2;
38200 * Placement constant - The resizing element is positioned above the splitter element
38204 Roo.bootstrap.SplitBar.TOP = 3;
38207 * Placement constant - The resizing element is positioned under splitter element
38211 Roo.bootstrap.SplitBar.BOTTOM = 4;
38212 Roo.namespace("Roo.bootstrap.layout");/*
38214 * Ext JS Library 1.1.1
38215 * Copyright(c) 2006-2007, Ext JS, LLC.
38217 * Originally Released Under LGPL - original licence link has changed is not relivant.
38220 * <script type="text/javascript">
38224 * @class Roo.bootstrap.layout.Manager
38225 * @extends Roo.bootstrap.Component
38226 * Base class for layout managers.
38228 Roo.bootstrap.layout.Manager = function(config)
38230 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38236 /** false to disable window resize monitoring @type Boolean */
38237 this.monitorWindowResize = true;
38242 * Fires when a layout is performed.
38243 * @param {Roo.LayoutManager} this
38247 * @event regionresized
38248 * Fires when the user resizes a region.
38249 * @param {Roo.LayoutRegion} region The resized region
38250 * @param {Number} newSize The new size (width for east/west, height for north/south)
38252 "regionresized" : true,
38254 * @event regioncollapsed
38255 * Fires when a region is collapsed.
38256 * @param {Roo.LayoutRegion} region The collapsed region
38258 "regioncollapsed" : true,
38260 * @event regionexpanded
38261 * Fires when a region is expanded.
38262 * @param {Roo.LayoutRegion} region The expanded region
38264 "regionexpanded" : true
38266 this.updating = false;
38269 this.el = Roo.get(config.el);
38275 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38280 monitorWindowResize : true,
38286 onRender : function(ct, position)
38289 this.el = Roo.get(ct);
38292 //this.fireEvent('render',this);
38296 initEvents: function()
38300 // ie scrollbar fix
38301 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38302 document.body.scroll = "no";
38303 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38304 this.el.position('relative');
38306 this.id = this.el.id;
38307 this.el.addClass("roo-layout-container");
38308 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38309 if(this.el.dom != document.body ) {
38310 this.el.on('resize', this.layout,this);
38311 this.el.on('show', this.layout,this);
38317 * Returns true if this layout is currently being updated
38318 * @return {Boolean}
38320 isUpdating : function(){
38321 return this.updating;
38325 * Suspend the LayoutManager from doing auto-layouts while
38326 * making multiple add or remove calls
38328 beginUpdate : function(){
38329 this.updating = true;
38333 * Restore auto-layouts and optionally disable the manager from performing a layout
38334 * @param {Boolean} noLayout true to disable a layout update
38336 endUpdate : function(noLayout){
38337 this.updating = false;
38343 layout: function(){
38347 onRegionResized : function(region, newSize){
38348 this.fireEvent("regionresized", region, newSize);
38352 onRegionCollapsed : function(region){
38353 this.fireEvent("regioncollapsed", region);
38356 onRegionExpanded : function(region){
38357 this.fireEvent("regionexpanded", region);
38361 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38362 * performs box-model adjustments.
38363 * @return {Object} The size as an object {width: (the width), height: (the height)}
38365 getViewSize : function()
38368 if(this.el.dom != document.body){
38369 size = this.el.getSize();
38371 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38373 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38374 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38379 * Returns the Element this layout is bound to.
38380 * @return {Roo.Element}
38382 getEl : function(){
38387 * Returns the specified region.
38388 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38389 * @return {Roo.LayoutRegion}
38391 getRegion : function(target){
38392 return this.regions[target.toLowerCase()];
38395 onWindowResize : function(){
38396 if(this.monitorWindowResize){
38403 * Ext JS Library 1.1.1
38404 * Copyright(c) 2006-2007, Ext JS, LLC.
38406 * Originally Released Under LGPL - original licence link has changed is not relivant.
38409 * <script type="text/javascript">
38412 * @class Roo.bootstrap.layout.Border
38413 * @extends Roo.bootstrap.layout.Manager
38415 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38416 * please see: examples/bootstrap/nested.html<br><br>
38418 <b>The container the layout is rendered into can be either the body element or any other element.
38419 If it is not the body element, the container needs to either be an absolute positioned element,
38420 or you will need to add "position:relative" to the css of the container. You will also need to specify
38421 the container size if it is not the body element.</b>
38424 * Create a new Border
38425 * @param {Object} config Configuration options
38427 Roo.bootstrap.layout.Border = function(config){
38428 config = config || {};
38429 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38433 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38434 if(config[region]){
38435 config[region].region = region;
38436 this.addRegion(config[region]);
38442 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38444 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38446 parent : false, // this might point to a 'nest' or a ???
38449 * Creates and adds a new region if it doesn't already exist.
38450 * @param {String} target The target region key (north, south, east, west or center).
38451 * @param {Object} config The regions config object
38452 * @return {BorderLayoutRegion} The new region
38454 addRegion : function(config)
38456 if(!this.regions[config.region]){
38457 var r = this.factory(config);
38458 this.bindRegion(r);
38460 return this.regions[config.region];
38464 bindRegion : function(r){
38465 this.regions[r.config.region] = r;
38467 r.on("visibilitychange", this.layout, this);
38468 r.on("paneladded", this.layout, this);
38469 r.on("panelremoved", this.layout, this);
38470 r.on("invalidated", this.layout, this);
38471 r.on("resized", this.onRegionResized, this);
38472 r.on("collapsed", this.onRegionCollapsed, this);
38473 r.on("expanded", this.onRegionExpanded, this);
38477 * Performs a layout update.
38479 layout : function()
38481 if(this.updating) {
38485 // render all the rebions if they have not been done alreayd?
38486 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38487 if(this.regions[region] && !this.regions[region].bodyEl){
38488 this.regions[region].onRender(this.el)
38492 var size = this.getViewSize();
38493 var w = size.width;
38494 var h = size.height;
38499 //var x = 0, y = 0;
38501 var rs = this.regions;
38502 var north = rs["north"];
38503 var south = rs["south"];
38504 var west = rs["west"];
38505 var east = rs["east"];
38506 var center = rs["center"];
38507 //if(this.hideOnLayout){ // not supported anymore
38508 //c.el.setStyle("display", "none");
38510 if(north && north.isVisible()){
38511 var b = north.getBox();
38512 var m = north.getMargins();
38513 b.width = w - (m.left+m.right);
38516 centerY = b.height + b.y + m.bottom;
38517 centerH -= centerY;
38518 north.updateBox(this.safeBox(b));
38520 if(south && south.isVisible()){
38521 var b = south.getBox();
38522 var m = south.getMargins();
38523 b.width = w - (m.left+m.right);
38525 var totalHeight = (b.height + m.top + m.bottom);
38526 b.y = h - totalHeight + m.top;
38527 centerH -= totalHeight;
38528 south.updateBox(this.safeBox(b));
38530 if(west && west.isVisible()){
38531 var b = west.getBox();
38532 var m = west.getMargins();
38533 b.height = centerH - (m.top+m.bottom);
38535 b.y = centerY + m.top;
38536 var totalWidth = (b.width + m.left + m.right);
38537 centerX += totalWidth;
38538 centerW -= totalWidth;
38539 west.updateBox(this.safeBox(b));
38541 if(east && east.isVisible()){
38542 var b = east.getBox();
38543 var m = east.getMargins();
38544 b.height = centerH - (m.top+m.bottom);
38545 var totalWidth = (b.width + m.left + m.right);
38546 b.x = w - totalWidth + m.left;
38547 b.y = centerY + m.top;
38548 centerW -= totalWidth;
38549 east.updateBox(this.safeBox(b));
38552 var m = center.getMargins();
38554 x: centerX + m.left,
38555 y: centerY + m.top,
38556 width: centerW - (m.left+m.right),
38557 height: centerH - (m.top+m.bottom)
38559 //if(this.hideOnLayout){
38560 //center.el.setStyle("display", "block");
38562 center.updateBox(this.safeBox(centerBox));
38565 this.fireEvent("layout", this);
38569 safeBox : function(box){
38570 box.width = Math.max(0, box.width);
38571 box.height = Math.max(0, box.height);
38576 * Adds a ContentPanel (or subclass) to this layout.
38577 * @param {String} target The target region key (north, south, east, west or center).
38578 * @param {Roo.ContentPanel} panel The panel to add
38579 * @return {Roo.ContentPanel} The added panel
38581 add : function(target, panel){
38583 target = target.toLowerCase();
38584 return this.regions[target].add(panel);
38588 * Remove a ContentPanel (or subclass) to this layout.
38589 * @param {String} target The target region key (north, south, east, west or center).
38590 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38591 * @return {Roo.ContentPanel} The removed panel
38593 remove : function(target, panel){
38594 target = target.toLowerCase();
38595 return this.regions[target].remove(panel);
38599 * Searches all regions for a panel with the specified id
38600 * @param {String} panelId
38601 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38603 findPanel : function(panelId){
38604 var rs = this.regions;
38605 for(var target in rs){
38606 if(typeof rs[target] != "function"){
38607 var p = rs[target].getPanel(panelId);
38617 * Searches all regions for a panel with the specified id and activates (shows) it.
38618 * @param {String/ContentPanel} panelId The panels id or the panel itself
38619 * @return {Roo.ContentPanel} The shown panel or null
38621 showPanel : function(panelId) {
38622 var rs = this.regions;
38623 for(var target in rs){
38624 var r = rs[target];
38625 if(typeof r != "function"){
38626 if(r.hasPanel(panelId)){
38627 return r.showPanel(panelId);
38635 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38636 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38639 restoreState : function(provider){
38641 provider = Roo.state.Manager;
38643 var sm = new Roo.LayoutStateManager();
38644 sm.init(this, provider);
38650 * Adds a xtype elements to the layout.
38654 xtype : 'ContentPanel',
38661 xtype : 'NestedLayoutPanel',
38667 items : [ ... list of content panels or nested layout panels.. ]
38671 * @param {Object} cfg Xtype definition of item to add.
38673 addxtype : function(cfg)
38675 // basically accepts a pannel...
38676 // can accept a layout region..!?!?
38677 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38680 // theory? children can only be panels??
38682 //if (!cfg.xtype.match(/Panel$/)) {
38687 if (typeof(cfg.region) == 'undefined') {
38688 Roo.log("Failed to add Panel, region was not set");
38692 var region = cfg.region;
38698 xitems = cfg.items;
38703 if ( region == 'center') {
38704 Roo.log("Center: " + cfg.title);
38710 case 'Content': // ContentPanel (el, cfg)
38711 case 'Scroll': // ContentPanel (el, cfg)
38713 cfg.autoCreate = cfg.autoCreate || true;
38714 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38716 // var el = this.el.createChild();
38717 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38720 this.add(region, ret);
38724 case 'TreePanel': // our new panel!
38725 cfg.el = this.el.createChild();
38726 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38727 this.add(region, ret);
38732 // create a new Layout (which is a Border Layout...
38734 var clayout = cfg.layout;
38735 clayout.el = this.el.createChild();
38736 clayout.items = clayout.items || [];
38740 // replace this exitems with the clayout ones..
38741 xitems = clayout.items;
38743 // force background off if it's in center...
38744 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38745 cfg.background = false;
38747 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38750 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38751 //console.log('adding nested layout panel ' + cfg.toSource());
38752 this.add(region, ret);
38753 nb = {}; /// find first...
38758 // needs grid and region
38760 //var el = this.getRegion(region).el.createChild();
38762 *var el = this.el.createChild();
38763 // create the grid first...
38764 cfg.grid.container = el;
38765 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38768 if (region == 'center' && this.active ) {
38769 cfg.background = false;
38772 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38774 this.add(region, ret);
38776 if (cfg.background) {
38777 // render grid on panel activation (if panel background)
38778 ret.on('activate', function(gp) {
38779 if (!gp.grid.rendered) {
38780 // gp.grid.render(el);
38784 // cfg.grid.render(el);
38790 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38791 // it was the old xcomponent building that caused this before.
38792 // espeically if border is the top element in the tree.
38802 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38804 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38805 this.add(region, ret);
38809 throw "Can not add '" + cfg.xtype + "' to Border";
38815 this.beginUpdate();
38819 Roo.each(xitems, function(i) {
38820 region = nb && i.region ? i.region : false;
38822 var add = ret.addxtype(i);
38825 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38826 if (!i.background) {
38827 abn[region] = nb[region] ;
38834 // make the last non-background panel active..
38835 //if (nb) { Roo.log(abn); }
38838 for(var r in abn) {
38839 region = this.getRegion(r);
38841 // tried using nb[r], but it does not work..
38843 region.showPanel(abn[r]);
38854 factory : function(cfg)
38857 var validRegions = Roo.bootstrap.layout.Border.regions;
38859 var target = cfg.region;
38862 var r = Roo.bootstrap.layout;
38866 return new r.North(cfg);
38868 return new r.South(cfg);
38870 return new r.East(cfg);
38872 return new r.West(cfg);
38874 return new r.Center(cfg);
38876 throw 'Layout region "'+target+'" not supported.';
38883 * Ext JS Library 1.1.1
38884 * Copyright(c) 2006-2007, Ext JS, LLC.
38886 * Originally Released Under LGPL - original licence link has changed is not relivant.
38889 * <script type="text/javascript">
38893 * @class Roo.bootstrap.layout.Basic
38894 * @extends Roo.util.Observable
38895 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38896 * and does not have a titlebar, tabs or any other features. All it does is size and position
38897 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38898 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38899 * @cfg {string} region the region that it inhabits..
38900 * @cfg {bool} skipConfig skip config?
38904 Roo.bootstrap.layout.Basic = function(config){
38906 this.mgr = config.mgr;
38908 this.position = config.region;
38910 var skipConfig = config.skipConfig;
38914 * @scope Roo.BasicLayoutRegion
38918 * @event beforeremove
38919 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38920 * @param {Roo.LayoutRegion} this
38921 * @param {Roo.ContentPanel} panel The panel
38922 * @param {Object} e The cancel event object
38924 "beforeremove" : true,
38926 * @event invalidated
38927 * Fires when the layout for this region is changed.
38928 * @param {Roo.LayoutRegion} this
38930 "invalidated" : true,
38932 * @event visibilitychange
38933 * Fires when this region is shown or hidden
38934 * @param {Roo.LayoutRegion} this
38935 * @param {Boolean} visibility true or false
38937 "visibilitychange" : true,
38939 * @event paneladded
38940 * Fires when a panel is added.
38941 * @param {Roo.LayoutRegion} this
38942 * @param {Roo.ContentPanel} panel The panel
38944 "paneladded" : true,
38946 * @event panelremoved
38947 * Fires when a panel is removed.
38948 * @param {Roo.LayoutRegion} this
38949 * @param {Roo.ContentPanel} panel The panel
38951 "panelremoved" : true,
38953 * @event beforecollapse
38954 * Fires when this region before collapse.
38955 * @param {Roo.LayoutRegion} this
38957 "beforecollapse" : true,
38960 * Fires when this region is collapsed.
38961 * @param {Roo.LayoutRegion} this
38963 "collapsed" : true,
38966 * Fires when this region is expanded.
38967 * @param {Roo.LayoutRegion} this
38972 * Fires when this region is slid into view.
38973 * @param {Roo.LayoutRegion} this
38975 "slideshow" : true,
38978 * Fires when this region slides out of view.
38979 * @param {Roo.LayoutRegion} this
38981 "slidehide" : true,
38983 * @event panelactivated
38984 * Fires when a panel is activated.
38985 * @param {Roo.LayoutRegion} this
38986 * @param {Roo.ContentPanel} panel The activated panel
38988 "panelactivated" : true,
38991 * Fires when the user resizes this region.
38992 * @param {Roo.LayoutRegion} this
38993 * @param {Number} newSize The new size (width for east/west, height for north/south)
38997 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38998 this.panels = new Roo.util.MixedCollection();
38999 this.panels.getKey = this.getPanelId.createDelegate(this);
39001 this.activePanel = null;
39002 // ensure listeners are added...
39004 if (config.listeners || config.events) {
39005 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
39006 listeners : config.listeners || {},
39007 events : config.events || {}
39011 if(skipConfig !== true){
39012 this.applyConfig(config);
39016 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
39018 getPanelId : function(p){
39022 applyConfig : function(config){
39023 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39024 this.config = config;
39029 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
39030 * the width, for horizontal (north, south) the height.
39031 * @param {Number} newSize The new width or height
39033 resizeTo : function(newSize){
39034 var el = this.el ? this.el :
39035 (this.activePanel ? this.activePanel.getEl() : null);
39037 switch(this.position){
39040 el.setWidth(newSize);
39041 this.fireEvent("resized", this, newSize);
39045 el.setHeight(newSize);
39046 this.fireEvent("resized", this, newSize);
39052 getBox : function(){
39053 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39056 getMargins : function(){
39057 return this.margins;
39060 updateBox : function(box){
39062 var el = this.activePanel.getEl();
39063 el.dom.style.left = box.x + "px";
39064 el.dom.style.top = box.y + "px";
39065 this.activePanel.setSize(box.width, box.height);
39069 * Returns the container element for this region.
39070 * @return {Roo.Element}
39072 getEl : function(){
39073 return this.activePanel;
39077 * Returns true if this region is currently visible.
39078 * @return {Boolean}
39080 isVisible : function(){
39081 return this.activePanel ? true : false;
39084 setActivePanel : function(panel){
39085 panel = this.getPanel(panel);
39086 if(this.activePanel && this.activePanel != panel){
39087 this.activePanel.setActiveState(false);
39088 this.activePanel.getEl().setLeftTop(-10000,-10000);
39090 this.activePanel = panel;
39091 panel.setActiveState(true);
39093 panel.setSize(this.box.width, this.box.height);
39095 this.fireEvent("panelactivated", this, panel);
39096 this.fireEvent("invalidated");
39100 * Show the specified panel.
39101 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39102 * @return {Roo.ContentPanel} The shown panel or null
39104 showPanel : function(panel){
39105 panel = this.getPanel(panel);
39107 this.setActivePanel(panel);
39113 * Get the active panel for this region.
39114 * @return {Roo.ContentPanel} The active panel or null
39116 getActivePanel : function(){
39117 return this.activePanel;
39121 * Add the passed ContentPanel(s)
39122 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39123 * @return {Roo.ContentPanel} The panel added (if only one was added)
39125 add : function(panel){
39126 if(arguments.length > 1){
39127 for(var i = 0, len = arguments.length; i < len; i++) {
39128 this.add(arguments[i]);
39132 if(this.hasPanel(panel)){
39133 this.showPanel(panel);
39136 var el = panel.getEl();
39137 if(el.dom.parentNode != this.mgr.el.dom){
39138 this.mgr.el.dom.appendChild(el.dom);
39140 if(panel.setRegion){
39141 panel.setRegion(this);
39143 this.panels.add(panel);
39144 el.setStyle("position", "absolute");
39145 if(!panel.background){
39146 this.setActivePanel(panel);
39147 if(this.config.initialSize && this.panels.getCount()==1){
39148 this.resizeTo(this.config.initialSize);
39151 this.fireEvent("paneladded", this, panel);
39156 * Returns true if the panel is in this region.
39157 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39158 * @return {Boolean}
39160 hasPanel : function(panel){
39161 if(typeof panel == "object"){ // must be panel obj
39162 panel = panel.getId();
39164 return this.getPanel(panel) ? true : false;
39168 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39169 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39170 * @param {Boolean} preservePanel Overrides the config preservePanel option
39171 * @return {Roo.ContentPanel} The panel that was removed
39173 remove : function(panel, preservePanel){
39174 panel = this.getPanel(panel);
39179 this.fireEvent("beforeremove", this, panel, e);
39180 if(e.cancel === true){
39183 var panelId = panel.getId();
39184 this.panels.removeKey(panelId);
39189 * Returns the panel specified or null if it's not in this region.
39190 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39191 * @return {Roo.ContentPanel}
39193 getPanel : function(id){
39194 if(typeof id == "object"){ // must be panel obj
39197 return this.panels.get(id);
39201 * Returns this regions position (north/south/east/west/center).
39204 getPosition: function(){
39205 return this.position;
39209 * Ext JS Library 1.1.1
39210 * Copyright(c) 2006-2007, Ext JS, LLC.
39212 * Originally Released Under LGPL - original licence link has changed is not relivant.
39215 * <script type="text/javascript">
39219 * @class Roo.bootstrap.layout.Region
39220 * @extends Roo.bootstrap.layout.Basic
39221 * This class represents a region in a layout manager.
39223 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39224 * @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})
39225 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
39226 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
39227 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
39228 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
39229 * @cfg {String} title The title for the region (overrides panel titles)
39230 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
39231 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39232 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
39233 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39234 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
39235 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39236 * the space available, similar to FireFox 1.5 tabs (defaults to false)
39237 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
39238 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
39239 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
39241 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
39242 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
39243 * @cfg {Boolean} disableTabTips True to disable tab tooltips
39244 * @cfg {Number} width For East/West panels
39245 * @cfg {Number} height For North/South panels
39246 * @cfg {Boolean} split To show the splitter
39247 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
39249 * @cfg {string} cls Extra CSS classes to add to region
39251 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
39252 * @cfg {string} region the region that it inhabits..
39255 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
39256 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
39258 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
39259 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
39260 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
39262 Roo.bootstrap.layout.Region = function(config)
39264 this.applyConfig(config);
39266 var mgr = config.mgr;
39267 var pos = config.region;
39268 config.skipConfig = true;
39269 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39272 this.onRender(mgr.el);
39275 this.visible = true;
39276 this.collapsed = false;
39277 this.unrendered_panels = [];
39280 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39282 position: '', // set by wrapper (eg. north/south etc..)
39283 unrendered_panels : null, // unrendered panels.
39285 tabPosition : false,
39287 mgr: false, // points to 'Border'
39290 createBody : function(){
39291 /** This region's body element
39292 * @type Roo.Element */
39293 this.bodyEl = this.el.createChild({
39295 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39299 onRender: function(ctr, pos)
39301 var dh = Roo.DomHelper;
39302 /** This region's container element
39303 * @type Roo.Element */
39304 this.el = dh.append(ctr.dom, {
39306 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39308 /** This region's title element
39309 * @type Roo.Element */
39311 this.titleEl = dh.append(this.el.dom, {
39313 unselectable: "on",
39314 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39316 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
39317 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39321 this.titleEl.enableDisplayMode();
39322 /** This region's title text element
39323 * @type HTMLElement */
39324 this.titleTextEl = this.titleEl.dom.firstChild;
39325 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39327 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39328 this.closeBtn.enableDisplayMode();
39329 this.closeBtn.on("click", this.closeClicked, this);
39330 this.closeBtn.hide();
39332 this.createBody(this.config);
39333 if(this.config.hideWhenEmpty){
39335 this.on("paneladded", this.validateVisibility, this);
39336 this.on("panelremoved", this.validateVisibility, this);
39338 if(this.autoScroll){
39339 this.bodyEl.setStyle("overflow", "auto");
39341 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39343 //if(c.titlebar !== false){
39344 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39345 this.titleEl.hide();
39347 this.titleEl.show();
39348 if(this.config.title){
39349 this.titleTextEl.innerHTML = this.config.title;
39353 if(this.config.collapsed){
39354 this.collapse(true);
39356 if(this.config.hidden){
39360 if (this.unrendered_panels && this.unrendered_panels.length) {
39361 for (var i =0;i< this.unrendered_panels.length; i++) {
39362 this.add(this.unrendered_panels[i]);
39364 this.unrendered_panels = null;
39370 applyConfig : function(c)
39373 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39374 var dh = Roo.DomHelper;
39375 if(c.titlebar !== false){
39376 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39377 this.collapseBtn.on("click", this.collapse, this);
39378 this.collapseBtn.enableDisplayMode();
39380 if(c.showPin === true || this.showPin){
39381 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39382 this.stickBtn.enableDisplayMode();
39383 this.stickBtn.on("click", this.expand, this);
39384 this.stickBtn.hide();
39389 /** This region's collapsed element
39390 * @type Roo.Element */
39393 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39394 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39397 if(c.floatable !== false){
39398 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39399 this.collapsedEl.on("click", this.collapseClick, this);
39402 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39403 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39404 id: "message", unselectable: "on", style:{"float":"left"}});
39405 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39407 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39408 this.expandBtn.on("click", this.expand, this);
39412 if(this.collapseBtn){
39413 this.collapseBtn.setVisible(c.collapsible == true);
39416 this.cmargins = c.cmargins || this.cmargins ||
39417 (this.position == "west" || this.position == "east" ?
39418 {top: 0, left: 2, right:2, bottom: 0} :
39419 {top: 2, left: 0, right:0, bottom: 2});
39421 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39424 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39426 this.autoScroll = c.autoScroll || false;
39431 this.duration = c.duration || .30;
39432 this.slideDuration = c.slideDuration || .45;
39437 * Returns true if this region is currently visible.
39438 * @return {Boolean}
39440 isVisible : function(){
39441 return this.visible;
39445 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39446 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39448 //setCollapsedTitle : function(title){
39449 // title = title || " ";
39450 // if(this.collapsedTitleTextEl){
39451 // this.collapsedTitleTextEl.innerHTML = title;
39455 getBox : function(){
39457 // if(!this.collapsed){
39458 b = this.el.getBox(false, true);
39460 // b = this.collapsedEl.getBox(false, true);
39465 getMargins : function(){
39466 return this.margins;
39467 //return this.collapsed ? this.cmargins : this.margins;
39470 highlight : function(){
39471 this.el.addClass("x-layout-panel-dragover");
39474 unhighlight : function(){
39475 this.el.removeClass("x-layout-panel-dragover");
39478 updateBox : function(box)
39480 if (!this.bodyEl) {
39481 return; // not rendered yet..
39485 if(!this.collapsed){
39486 this.el.dom.style.left = box.x + "px";
39487 this.el.dom.style.top = box.y + "px";
39488 this.updateBody(box.width, box.height);
39490 this.collapsedEl.dom.style.left = box.x + "px";
39491 this.collapsedEl.dom.style.top = box.y + "px";
39492 this.collapsedEl.setSize(box.width, box.height);
39495 this.tabs.autoSizeTabs();
39499 updateBody : function(w, h)
39502 this.el.setWidth(w);
39503 w -= this.el.getBorderWidth("rl");
39504 if(this.config.adjustments){
39505 w += this.config.adjustments[0];
39508 if(h !== null && h > 0){
39509 this.el.setHeight(h);
39510 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39511 h -= this.el.getBorderWidth("tb");
39512 if(this.config.adjustments){
39513 h += this.config.adjustments[1];
39515 this.bodyEl.setHeight(h);
39517 h = this.tabs.syncHeight(h);
39520 if(this.panelSize){
39521 w = w !== null ? w : this.panelSize.width;
39522 h = h !== null ? h : this.panelSize.height;
39524 if(this.activePanel){
39525 var el = this.activePanel.getEl();
39526 w = w !== null ? w : el.getWidth();
39527 h = h !== null ? h : el.getHeight();
39528 this.panelSize = {width: w, height: h};
39529 this.activePanel.setSize(w, h);
39531 if(Roo.isIE && this.tabs){
39532 this.tabs.el.repaint();
39537 * Returns the container element for this region.
39538 * @return {Roo.Element}
39540 getEl : function(){
39545 * Hides this region.
39548 //if(!this.collapsed){
39549 this.el.dom.style.left = "-2000px";
39552 // this.collapsedEl.dom.style.left = "-2000px";
39553 // this.collapsedEl.hide();
39555 this.visible = false;
39556 this.fireEvent("visibilitychange", this, false);
39560 * Shows this region if it was previously hidden.
39563 //if(!this.collapsed){
39566 // this.collapsedEl.show();
39568 this.visible = true;
39569 this.fireEvent("visibilitychange", this, true);
39572 closeClicked : function(){
39573 if(this.activePanel){
39574 this.remove(this.activePanel);
39578 collapseClick : function(e){
39580 e.stopPropagation();
39583 e.stopPropagation();
39589 * Collapses this region.
39590 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39593 collapse : function(skipAnim, skipCheck = false){
39594 if(this.collapsed) {
39598 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39600 this.collapsed = true;
39602 this.split.el.hide();
39604 if(this.config.animate && skipAnim !== true){
39605 this.fireEvent("invalidated", this);
39606 this.animateCollapse();
39608 this.el.setLocation(-20000,-20000);
39610 this.collapsedEl.show();
39611 this.fireEvent("collapsed", this);
39612 this.fireEvent("invalidated", this);
39618 animateCollapse : function(){
39623 * Expands this region if it was previously collapsed.
39624 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39625 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39628 expand : function(e, skipAnim){
39630 e.stopPropagation();
39632 if(!this.collapsed || this.el.hasActiveFx()) {
39636 this.afterSlideIn();
39639 this.collapsed = false;
39640 if(this.config.animate && skipAnim !== true){
39641 this.animateExpand();
39645 this.split.el.show();
39647 this.collapsedEl.setLocation(-2000,-2000);
39648 this.collapsedEl.hide();
39649 this.fireEvent("invalidated", this);
39650 this.fireEvent("expanded", this);
39654 animateExpand : function(){
39658 initTabs : function()
39660 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39662 var ts = new Roo.bootstrap.panel.Tabs({
39663 el: this.bodyEl.dom,
39665 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39666 disableTooltips: this.config.disableTabTips,
39667 toolbar : this.config.toolbar
39670 if(this.config.hideTabs){
39671 ts.stripWrap.setDisplayed(false);
39674 ts.resizeTabs = this.config.resizeTabs === true;
39675 ts.minTabWidth = this.config.minTabWidth || 40;
39676 ts.maxTabWidth = this.config.maxTabWidth || 250;
39677 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39678 ts.monitorResize = false;
39679 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39680 ts.bodyEl.addClass('roo-layout-tabs-body');
39681 this.panels.each(this.initPanelAsTab, this);
39684 initPanelAsTab : function(panel){
39685 var ti = this.tabs.addTab(
39689 this.config.closeOnTab && panel.isClosable(),
39692 if(panel.tabTip !== undefined){
39693 ti.setTooltip(panel.tabTip);
39695 ti.on("activate", function(){
39696 this.setActivePanel(panel);
39699 if(this.config.closeOnTab){
39700 ti.on("beforeclose", function(t, e){
39702 this.remove(panel);
39706 panel.tabItem = ti;
39711 updatePanelTitle : function(panel, title)
39713 if(this.activePanel == panel){
39714 this.updateTitle(title);
39717 var ti = this.tabs.getTab(panel.getEl().id);
39719 if(panel.tabTip !== undefined){
39720 ti.setTooltip(panel.tabTip);
39725 updateTitle : function(title){
39726 if(this.titleTextEl && !this.config.title){
39727 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39731 setActivePanel : function(panel)
39733 panel = this.getPanel(panel);
39734 if(this.activePanel && this.activePanel != panel){
39735 if(this.activePanel.setActiveState(false) === false){
39739 this.activePanel = panel;
39740 panel.setActiveState(true);
39741 if(this.panelSize){
39742 panel.setSize(this.panelSize.width, this.panelSize.height);
39745 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39747 this.updateTitle(panel.getTitle());
39749 this.fireEvent("invalidated", this);
39751 this.fireEvent("panelactivated", this, panel);
39755 * Shows the specified panel.
39756 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39757 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39759 showPanel : function(panel)
39761 panel = this.getPanel(panel);
39764 var tab = this.tabs.getTab(panel.getEl().id);
39765 if(tab.isHidden()){
39766 this.tabs.unhideTab(tab.id);
39770 this.setActivePanel(panel);
39777 * Get the active panel for this region.
39778 * @return {Roo.ContentPanel} The active panel or null
39780 getActivePanel : function(){
39781 return this.activePanel;
39784 validateVisibility : function(){
39785 if(this.panels.getCount() < 1){
39786 this.updateTitle(" ");
39787 this.closeBtn.hide();
39790 if(!this.isVisible()){
39797 * Adds the passed ContentPanel(s) to this region.
39798 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39799 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39801 add : function(panel)
39803 if(arguments.length > 1){
39804 for(var i = 0, len = arguments.length; i < len; i++) {
39805 this.add(arguments[i]);
39810 // if we have not been rendered yet, then we can not really do much of this..
39811 if (!this.bodyEl) {
39812 this.unrendered_panels.push(panel);
39819 if(this.hasPanel(panel)){
39820 this.showPanel(panel);
39823 panel.setRegion(this);
39824 this.panels.add(panel);
39825 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39826 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39827 // and hide them... ???
39828 this.bodyEl.dom.appendChild(panel.getEl().dom);
39829 if(panel.background !== true){
39830 this.setActivePanel(panel);
39832 this.fireEvent("paneladded", this, panel);
39839 this.initPanelAsTab(panel);
39843 if(panel.background !== true){
39844 this.tabs.activate(panel.getEl().id);
39846 this.fireEvent("paneladded", this, panel);
39851 * Hides the tab for the specified panel.
39852 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39854 hidePanel : function(panel){
39855 if(this.tabs && (panel = this.getPanel(panel))){
39856 this.tabs.hideTab(panel.getEl().id);
39861 * Unhides the tab for a previously hidden panel.
39862 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39864 unhidePanel : function(panel){
39865 if(this.tabs && (panel = this.getPanel(panel))){
39866 this.tabs.unhideTab(panel.getEl().id);
39870 clearPanels : function(){
39871 while(this.panels.getCount() > 0){
39872 this.remove(this.panels.first());
39877 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39878 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39879 * @param {Boolean} preservePanel Overrides the config preservePanel option
39880 * @return {Roo.ContentPanel} The panel that was removed
39882 remove : function(panel, preservePanel)
39884 panel = this.getPanel(panel);
39889 this.fireEvent("beforeremove", this, panel, e);
39890 if(e.cancel === true){
39893 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39894 var panelId = panel.getId();
39895 this.panels.removeKey(panelId);
39897 document.body.appendChild(panel.getEl().dom);
39900 this.tabs.removeTab(panel.getEl().id);
39901 }else if (!preservePanel){
39902 this.bodyEl.dom.removeChild(panel.getEl().dom);
39904 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39905 var p = this.panels.first();
39906 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39907 tempEl.appendChild(p.getEl().dom);
39908 this.bodyEl.update("");
39909 this.bodyEl.dom.appendChild(p.getEl().dom);
39911 this.updateTitle(p.getTitle());
39913 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39914 this.setActivePanel(p);
39916 panel.setRegion(null);
39917 if(this.activePanel == panel){
39918 this.activePanel = null;
39920 if(this.config.autoDestroy !== false && preservePanel !== true){
39921 try{panel.destroy();}catch(e){}
39923 this.fireEvent("panelremoved", this, panel);
39928 * Returns the TabPanel component used by this region
39929 * @return {Roo.TabPanel}
39931 getTabs : function(){
39935 createTool : function(parentEl, className){
39936 var btn = Roo.DomHelper.append(parentEl, {
39938 cls: "x-layout-tools-button",
39941 cls: "roo-layout-tools-button-inner " + className,
39945 btn.addClassOnOver("roo-layout-tools-button-over");
39950 * Ext JS Library 1.1.1
39951 * Copyright(c) 2006-2007, Ext JS, LLC.
39953 * Originally Released Under LGPL - original licence link has changed is not relivant.
39956 * <script type="text/javascript">
39962 * @class Roo.SplitLayoutRegion
39963 * @extends Roo.LayoutRegion
39964 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39966 Roo.bootstrap.layout.Split = function(config){
39967 this.cursor = config.cursor;
39968 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39971 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39973 splitTip : "Drag to resize.",
39974 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39975 useSplitTips : false,
39977 applyConfig : function(config){
39978 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39981 onRender : function(ctr,pos) {
39983 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39984 if(!this.config.split){
39989 var splitEl = Roo.DomHelper.append(ctr.dom, {
39991 id: this.el.id + "-split",
39992 cls: "roo-layout-split roo-layout-split-"+this.position,
39995 /** The SplitBar for this region
39996 * @type Roo.SplitBar */
39997 // does not exist yet...
39998 Roo.log([this.position, this.orientation]);
40000 this.split = new Roo.bootstrap.SplitBar({
40001 dragElement : splitEl,
40002 resizingElement: this.el,
40003 orientation : this.orientation
40006 this.split.on("moved", this.onSplitMove, this);
40007 this.split.useShim = this.config.useShim === true;
40008 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
40009 if(this.useSplitTips){
40010 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
40012 //if(config.collapsible){
40013 // this.split.el.on("dblclick", this.collapse, this);
40016 if(typeof this.config.minSize != "undefined"){
40017 this.split.minSize = this.config.minSize;
40019 if(typeof this.config.maxSize != "undefined"){
40020 this.split.maxSize = this.config.maxSize;
40022 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
40023 this.hideSplitter();
40028 getHMaxSize : function(){
40029 var cmax = this.config.maxSize || 10000;
40030 var center = this.mgr.getRegion("center");
40031 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
40034 getVMaxSize : function(){
40035 var cmax = this.config.maxSize || 10000;
40036 var center = this.mgr.getRegion("center");
40037 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
40040 onSplitMove : function(split, newSize){
40041 this.fireEvent("resized", this, newSize);
40045 * Returns the {@link Roo.SplitBar} for this region.
40046 * @return {Roo.SplitBar}
40048 getSplitBar : function(){
40053 this.hideSplitter();
40054 Roo.bootstrap.layout.Split.superclass.hide.call(this);
40057 hideSplitter : function(){
40059 this.split.el.setLocation(-2000,-2000);
40060 this.split.el.hide();
40066 this.split.el.show();
40068 Roo.bootstrap.layout.Split.superclass.show.call(this);
40071 beforeSlide: function(){
40072 if(Roo.isGecko){// firefox overflow auto bug workaround
40073 this.bodyEl.clip();
40075 this.tabs.bodyEl.clip();
40077 if(this.activePanel){
40078 this.activePanel.getEl().clip();
40080 if(this.activePanel.beforeSlide){
40081 this.activePanel.beforeSlide();
40087 afterSlide : function(){
40088 if(Roo.isGecko){// firefox overflow auto bug workaround
40089 this.bodyEl.unclip();
40091 this.tabs.bodyEl.unclip();
40093 if(this.activePanel){
40094 this.activePanel.getEl().unclip();
40095 if(this.activePanel.afterSlide){
40096 this.activePanel.afterSlide();
40102 initAutoHide : function(){
40103 if(this.autoHide !== false){
40104 if(!this.autoHideHd){
40105 var st = new Roo.util.DelayedTask(this.slideIn, this);
40106 this.autoHideHd = {
40107 "mouseout": function(e){
40108 if(!e.within(this.el, true)){
40112 "mouseover" : function(e){
40118 this.el.on(this.autoHideHd);
40122 clearAutoHide : function(){
40123 if(this.autoHide !== false){
40124 this.el.un("mouseout", this.autoHideHd.mouseout);
40125 this.el.un("mouseover", this.autoHideHd.mouseover);
40129 clearMonitor : function(){
40130 Roo.get(document).un("click", this.slideInIf, this);
40133 // these names are backwards but not changed for compat
40134 slideOut : function(){
40135 if(this.isSlid || this.el.hasActiveFx()){
40138 this.isSlid = true;
40139 if(this.collapseBtn){
40140 this.collapseBtn.hide();
40142 this.closeBtnState = this.closeBtn.getStyle('display');
40143 this.closeBtn.hide();
40145 this.stickBtn.show();
40148 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40149 this.beforeSlide();
40150 this.el.setStyle("z-index", 10001);
40151 this.el.slideIn(this.getSlideAnchor(), {
40152 callback: function(){
40154 this.initAutoHide();
40155 Roo.get(document).on("click", this.slideInIf, this);
40156 this.fireEvent("slideshow", this);
40163 afterSlideIn : function(){
40164 this.clearAutoHide();
40165 this.isSlid = false;
40166 this.clearMonitor();
40167 this.el.setStyle("z-index", "");
40168 if(this.collapseBtn){
40169 this.collapseBtn.show();
40171 this.closeBtn.setStyle('display', this.closeBtnState);
40173 this.stickBtn.hide();
40175 this.fireEvent("slidehide", this);
40178 slideIn : function(cb){
40179 if(!this.isSlid || this.el.hasActiveFx()){
40183 this.isSlid = false;
40184 this.beforeSlide();
40185 this.el.slideOut(this.getSlideAnchor(), {
40186 callback: function(){
40187 this.el.setLeftTop(-10000, -10000);
40189 this.afterSlideIn();
40197 slideInIf : function(e){
40198 if(!e.within(this.el)){
40203 animateCollapse : function(){
40204 this.beforeSlide();
40205 this.el.setStyle("z-index", 20000);
40206 var anchor = this.getSlideAnchor();
40207 this.el.slideOut(anchor, {
40208 callback : function(){
40209 this.el.setStyle("z-index", "");
40210 this.collapsedEl.slideIn(anchor, {duration:.3});
40212 this.el.setLocation(-10000,-10000);
40214 this.fireEvent("collapsed", this);
40221 animateExpand : function(){
40222 this.beforeSlide();
40223 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40224 this.el.setStyle("z-index", 20000);
40225 this.collapsedEl.hide({
40228 this.el.slideIn(this.getSlideAnchor(), {
40229 callback : function(){
40230 this.el.setStyle("z-index", "");
40233 this.split.el.show();
40235 this.fireEvent("invalidated", this);
40236 this.fireEvent("expanded", this);
40264 getAnchor : function(){
40265 return this.anchors[this.position];
40268 getCollapseAnchor : function(){
40269 return this.canchors[this.position];
40272 getSlideAnchor : function(){
40273 return this.sanchors[this.position];
40276 getAlignAdj : function(){
40277 var cm = this.cmargins;
40278 switch(this.position){
40294 getExpandAdj : function(){
40295 var c = this.collapsedEl, cm = this.cmargins;
40296 switch(this.position){
40298 return [-(cm.right+c.getWidth()+cm.left), 0];
40301 return [cm.right+c.getWidth()+cm.left, 0];
40304 return [0, -(cm.top+cm.bottom+c.getHeight())];
40307 return [0, cm.top+cm.bottom+c.getHeight()];
40313 * Ext JS Library 1.1.1
40314 * Copyright(c) 2006-2007, Ext JS, LLC.
40316 * Originally Released Under LGPL - original licence link has changed is not relivant.
40319 * <script type="text/javascript">
40322 * These classes are private internal classes
40324 Roo.bootstrap.layout.Center = function(config){
40325 config.region = "center";
40326 Roo.bootstrap.layout.Region.call(this, config);
40327 this.visible = true;
40328 this.minWidth = config.minWidth || 20;
40329 this.minHeight = config.minHeight || 20;
40332 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40334 // center panel can't be hidden
40338 // center panel can't be hidden
40341 getMinWidth: function(){
40342 return this.minWidth;
40345 getMinHeight: function(){
40346 return this.minHeight;
40360 Roo.bootstrap.layout.North = function(config)
40362 config.region = 'north';
40363 config.cursor = 'n-resize';
40365 Roo.bootstrap.layout.Split.call(this, config);
40369 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40370 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40371 this.split.el.addClass("roo-layout-split-v");
40373 //var size = config.initialSize || config.height;
40374 //if(this.el && typeof size != "undefined"){
40375 // this.el.setHeight(size);
40378 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40380 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40383 onRender : function(ctr, pos)
40385 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40386 var size = this.config.initialSize || this.config.height;
40387 if(this.el && typeof size != "undefined"){
40388 this.el.setHeight(size);
40393 getBox : function(){
40394 if(this.collapsed){
40395 return this.collapsedEl.getBox();
40397 var box = this.el.getBox();
40399 box.height += this.split.el.getHeight();
40404 updateBox : function(box){
40405 if(this.split && !this.collapsed){
40406 box.height -= this.split.el.getHeight();
40407 this.split.el.setLeft(box.x);
40408 this.split.el.setTop(box.y+box.height);
40409 this.split.el.setWidth(box.width);
40411 if(this.collapsed){
40412 this.updateBody(box.width, null);
40414 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40422 Roo.bootstrap.layout.South = function(config){
40423 config.region = 'south';
40424 config.cursor = 's-resize';
40425 Roo.bootstrap.layout.Split.call(this, config);
40427 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40428 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40429 this.split.el.addClass("roo-layout-split-v");
40434 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40435 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40437 onRender : function(ctr, pos)
40439 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40440 var size = this.config.initialSize || this.config.height;
40441 if(this.el && typeof size != "undefined"){
40442 this.el.setHeight(size);
40447 getBox : function(){
40448 if(this.collapsed){
40449 return this.collapsedEl.getBox();
40451 var box = this.el.getBox();
40453 var sh = this.split.el.getHeight();
40460 updateBox : function(box){
40461 if(this.split && !this.collapsed){
40462 var sh = this.split.el.getHeight();
40465 this.split.el.setLeft(box.x);
40466 this.split.el.setTop(box.y-sh);
40467 this.split.el.setWidth(box.width);
40469 if(this.collapsed){
40470 this.updateBody(box.width, null);
40472 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40476 Roo.bootstrap.layout.East = function(config){
40477 config.region = "east";
40478 config.cursor = "e-resize";
40479 Roo.bootstrap.layout.Split.call(this, config);
40481 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40482 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40483 this.split.el.addClass("roo-layout-split-h");
40487 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40488 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40490 onRender : function(ctr, pos)
40492 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40493 var size = this.config.initialSize || this.config.width;
40494 if(this.el && typeof size != "undefined"){
40495 this.el.setWidth(size);
40500 getBox : function(){
40501 if(this.collapsed){
40502 return this.collapsedEl.getBox();
40504 var box = this.el.getBox();
40506 var sw = this.split.el.getWidth();
40513 updateBox : function(box){
40514 if(this.split && !this.collapsed){
40515 var sw = this.split.el.getWidth();
40517 this.split.el.setLeft(box.x);
40518 this.split.el.setTop(box.y);
40519 this.split.el.setHeight(box.height);
40522 if(this.collapsed){
40523 this.updateBody(null, box.height);
40525 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40529 Roo.bootstrap.layout.West = function(config){
40530 config.region = "west";
40531 config.cursor = "w-resize";
40533 Roo.bootstrap.layout.Split.call(this, config);
40535 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40536 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40537 this.split.el.addClass("roo-layout-split-h");
40541 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40542 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40544 onRender: function(ctr, pos)
40546 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40547 var size = this.config.initialSize || this.config.width;
40548 if(typeof size != "undefined"){
40549 this.el.setWidth(size);
40553 getBox : function(){
40554 if(this.collapsed){
40555 return this.collapsedEl.getBox();
40557 var box = this.el.getBox();
40558 if (box.width == 0) {
40559 box.width = this.config.width; // kludge?
40562 box.width += this.split.el.getWidth();
40567 updateBox : function(box){
40568 if(this.split && !this.collapsed){
40569 var sw = this.split.el.getWidth();
40571 this.split.el.setLeft(box.x+box.width);
40572 this.split.el.setTop(box.y);
40573 this.split.el.setHeight(box.height);
40575 if(this.collapsed){
40576 this.updateBody(null, box.height);
40578 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40580 });Roo.namespace("Roo.bootstrap.panel");/*
40582 * Ext JS Library 1.1.1
40583 * Copyright(c) 2006-2007, Ext JS, LLC.
40585 * Originally Released Under LGPL - original licence link has changed is not relivant.
40588 * <script type="text/javascript">
40591 * @class Roo.bootstrap.paenl.Content
40592 * @extends Roo.util.Observable
40594 * @children Roo.bootstrap.Component
40595 * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40596 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40597 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40598 * @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
40599 * @cfg {Boolean} closable True if the panel can be closed/removed
40600 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40601 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40602 * @cfg {Toolbar} toolbar A toolbar for this panel
40603 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40604 * @cfg {String} title The title for this panel
40605 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40606 * @cfg {String} url Calls {@link #setUrl} with this value
40607 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40608 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40609 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40610 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40611 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40612 * @cfg {Boolean} badges render the badges
40613 * @cfg {String} cls extra classes to use
40614 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40617 * Create a new ContentPanel.
40618 * @param {String/Object} config A string to set only the title or a config object
40621 Roo.bootstrap.panel.Content = function( config){
40623 this.tpl = config.tpl || false;
40625 var el = config.el;
40626 var content = config.content;
40628 if(config.autoCreate){ // xtype is available if this is called from factory
40631 this.el = Roo.get(el);
40632 if(!this.el && config && config.autoCreate){
40633 if(typeof config.autoCreate == "object"){
40634 if(!config.autoCreate.id){
40635 config.autoCreate.id = config.id||el;
40637 this.el = Roo.DomHelper.append(document.body,
40638 config.autoCreate, true);
40642 cls: (config.cls || '') +
40643 (config.background ? ' bg-' + config.background : '') +
40644 " roo-layout-inactive-content",
40647 if (config.iframe) {
40651 style : 'border: 0px',
40652 src : 'about:blank'
40658 elcfg.html = config.html;
40662 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40663 if (config.iframe) {
40664 this.iframeEl = this.el.select('iframe',true).first();
40669 this.closable = false;
40670 this.loaded = false;
40671 this.active = false;
40674 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40676 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40678 this.wrapEl = this.el; //this.el.wrap();
40680 if (config.toolbar.items) {
40681 ti = config.toolbar.items ;
40682 delete config.toolbar.items ;
40686 this.toolbar.render(this.wrapEl, 'before');
40687 for(var i =0;i < ti.length;i++) {
40688 // Roo.log(['add child', items[i]]);
40689 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40691 this.toolbar.items = nitems;
40692 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40693 delete config.toolbar;
40697 // xtype created footer. - not sure if will work as we normally have to render first..
40698 if (this.footer && !this.footer.el && this.footer.xtype) {
40699 if (!this.wrapEl) {
40700 this.wrapEl = this.el.wrap();
40703 this.footer.container = this.wrapEl.createChild();
40705 this.footer = Roo.factory(this.footer, Roo);
40710 if(typeof config == "string"){
40711 this.title = config;
40713 Roo.apply(this, config);
40717 this.resizeEl = Roo.get(this.resizeEl, true);
40719 this.resizeEl = this.el;
40721 // handle view.xtype
40729 * Fires when this panel is activated.
40730 * @param {Roo.ContentPanel} this
40734 * @event deactivate
40735 * Fires when this panel is activated.
40736 * @param {Roo.ContentPanel} this
40738 "deactivate" : true,
40742 * Fires when this panel is resized if fitToFrame is true.
40743 * @param {Roo.ContentPanel} this
40744 * @param {Number} width The width after any component adjustments
40745 * @param {Number} height The height after any component adjustments
40751 * Fires when this tab is created
40752 * @param {Roo.ContentPanel} this
40758 * Fires when this content is scrolled
40759 * @param {Roo.ContentPanel} this
40760 * @param {Event} scrollEvent
40771 if(this.autoScroll && !this.iframe){
40772 this.resizeEl.setStyle("overflow", "auto");
40773 this.resizeEl.on('scroll', this.onScroll, this);
40775 // fix randome scrolling
40776 //this.el.on('scroll', function() {
40777 // Roo.log('fix random scolling');
40778 // this.scrollTo('top',0);
40781 content = content || this.content;
40783 this.setContent(content);
40785 if(config && config.url){
40786 this.setUrl(this.url, this.params, this.loadOnce);
40791 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40793 if (this.view && typeof(this.view.xtype) != 'undefined') {
40794 this.view.el = this.el.appendChild(document.createElement("div"));
40795 this.view = Roo.factory(this.view);
40796 this.view.render && this.view.render(false, '');
40800 this.fireEvent('render', this);
40803 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40813 /* Resize Element - use this to work out scroll etc. */
40816 setRegion : function(region){
40817 this.region = region;
40818 this.setActiveClass(region && !this.background);
40822 setActiveClass: function(state)
40825 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40826 this.el.setStyle('position','relative');
40828 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40829 this.el.setStyle('position', 'absolute');
40834 * Returns the toolbar for this Panel if one was configured.
40835 * @return {Roo.Toolbar}
40837 getToolbar : function(){
40838 return this.toolbar;
40841 setActiveState : function(active)
40843 this.active = active;
40844 this.setActiveClass(active);
40846 if(this.fireEvent("deactivate", this) === false){
40851 this.fireEvent("activate", this);
40855 * Updates this panel's element (not for iframe)
40856 * @param {String} content The new content
40857 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40859 setContent : function(content, loadScripts){
40864 this.el.update(content, loadScripts);
40867 ignoreResize : function(w, h){
40868 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40871 this.lastSize = {width: w, height: h};
40876 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40877 * @return {Roo.UpdateManager} The UpdateManager
40879 getUpdateManager : function(){
40883 return this.el.getUpdateManager();
40886 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40887 * Does not work with IFRAME contents
40888 * @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:
40891 url: "your-url.php",
40892 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40893 callback: yourFunction,
40894 scope: yourObject, //(optional scope)
40897 text: "Loading...",
40903 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40904 * 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.
40905 * @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}
40906 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40907 * @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.
40908 * @return {Roo.ContentPanel} this
40916 var um = this.el.getUpdateManager();
40917 um.update.apply(um, arguments);
40923 * 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.
40924 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40925 * @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)
40926 * @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)
40927 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40929 setUrl : function(url, params, loadOnce){
40931 this.iframeEl.dom.src = url;
40935 if(this.refreshDelegate){
40936 this.removeListener("activate", this.refreshDelegate);
40938 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40939 this.on("activate", this.refreshDelegate);
40940 return this.el.getUpdateManager();
40943 _handleRefresh : function(url, params, loadOnce){
40944 if(!loadOnce || !this.loaded){
40945 var updater = this.el.getUpdateManager();
40946 updater.update(url, params, this._setLoaded.createDelegate(this));
40950 _setLoaded : function(){
40951 this.loaded = true;
40955 * Returns this panel's id
40958 getId : function(){
40963 * Returns this panel's element - used by regiosn to add.
40964 * @return {Roo.Element}
40966 getEl : function(){
40967 return this.wrapEl || this.el;
40972 adjustForComponents : function(width, height)
40974 //Roo.log('adjustForComponents ');
40975 if(this.resizeEl != this.el){
40976 width -= this.el.getFrameWidth('lr');
40977 height -= this.el.getFrameWidth('tb');
40980 var te = this.toolbar.getEl();
40981 te.setWidth(width);
40982 height -= te.getHeight();
40985 var te = this.footer.getEl();
40986 te.setWidth(width);
40987 height -= te.getHeight();
40991 if(this.adjustments){
40992 width += this.adjustments[0];
40993 height += this.adjustments[1];
40995 return {"width": width, "height": height};
40998 setSize : function(width, height){
40999 if(this.fitToFrame && !this.ignoreResize(width, height)){
41000 if(this.fitContainer && this.resizeEl != this.el){
41001 this.el.setSize(width, height);
41003 var size = this.adjustForComponents(width, height);
41005 this.iframeEl.setSize(width,height);
41008 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
41009 this.fireEvent('resize', this, size.width, size.height);
41016 * Returns this panel's title
41019 getTitle : function(){
41021 if (typeof(this.title) != 'object') {
41026 for (var k in this.title) {
41027 if (!this.title.hasOwnProperty(k)) {
41031 if (k.indexOf('-') >= 0) {
41032 var s = k.split('-');
41033 for (var i = 0; i<s.length; i++) {
41034 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
41037 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
41044 * Set this panel's title
41045 * @param {String} title
41047 setTitle : function(title){
41048 this.title = title;
41050 this.region.updatePanelTitle(this, title);
41055 * Returns true is this panel was configured to be closable
41056 * @return {Boolean}
41058 isClosable : function(){
41059 return this.closable;
41062 beforeSlide : function(){
41064 this.resizeEl.clip();
41067 afterSlide : function(){
41069 this.resizeEl.unclip();
41073 * Force a content refresh from the URL specified in the {@link #setUrl} method.
41074 * Will fail silently if the {@link #setUrl} method has not been called.
41075 * This does not activate the panel, just updates its content.
41077 refresh : function(){
41078 if(this.refreshDelegate){
41079 this.loaded = false;
41080 this.refreshDelegate();
41085 * Destroys this panel
41087 destroy : function(){
41088 this.el.removeAllListeners();
41089 var tempEl = document.createElement("span");
41090 tempEl.appendChild(this.el.dom);
41091 tempEl.innerHTML = "";
41097 * form - if the content panel contains a form - this is a reference to it.
41098 * @type {Roo.form.Form}
41102 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41103 * This contains a reference to it.
41109 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41119 * @param {Object} cfg Xtype definition of item to add.
41123 getChildContainer: function () {
41124 return this.getEl();
41128 onScroll : function(e)
41130 this.fireEvent('scroll', this, e);
41135 var ret = new Roo.factory(cfg);
41140 if (cfg.xtype.match(/^Form$/)) {
41143 //if (this.footer) {
41144 // el = this.footer.container.insertSibling(false, 'before');
41146 el = this.el.createChild();
41149 this.form = new Roo.form.Form(cfg);
41152 if ( this.form.allItems.length) {
41153 this.form.render(el.dom);
41157 // should only have one of theses..
41158 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41159 // views.. should not be just added - used named prop 'view''
41161 cfg.el = this.el.appendChild(document.createElement("div"));
41164 var ret = new Roo.factory(cfg);
41166 ret.render && ret.render(false, ''); // render blank..
41176 * @class Roo.bootstrap.panel.Grid
41177 * @extends Roo.bootstrap.panel.Content
41179 * Create a new GridPanel.
41180 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41181 * @param {Object} config A the config object
41187 Roo.bootstrap.panel.Grid = function(config)
41191 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41192 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41194 config.el = this.wrapper;
41195 //this.el = this.wrapper;
41197 if (config.container) {
41198 // ctor'ed from a Border/panel.grid
41201 this.wrapper.setStyle("overflow", "hidden");
41202 this.wrapper.addClass('roo-grid-container');
41207 if(config.toolbar){
41208 var tool_el = this.wrapper.createChild();
41209 this.toolbar = Roo.factory(config.toolbar);
41211 if (config.toolbar.items) {
41212 ti = config.toolbar.items ;
41213 delete config.toolbar.items ;
41217 this.toolbar.render(tool_el);
41218 for(var i =0;i < ti.length;i++) {
41219 // Roo.log(['add child', items[i]]);
41220 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41222 this.toolbar.items = nitems;
41224 delete config.toolbar;
41227 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41228 config.grid.scrollBody = true;;
41229 config.grid.monitorWindowResize = false; // turn off autosizing
41230 config.grid.autoHeight = false;
41231 config.grid.autoWidth = false;
41233 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41235 if (config.background) {
41236 // render grid on panel activation (if panel background)
41237 this.on('activate', function(gp) {
41238 if (!gp.grid.rendered) {
41239 gp.grid.render(this.wrapper);
41240 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41245 this.grid.render(this.wrapper);
41246 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41249 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41250 // ??? needed ??? config.el = this.wrapper;
41255 // xtype created footer. - not sure if will work as we normally have to render first..
41256 if (this.footer && !this.footer.el && this.footer.xtype) {
41258 var ctr = this.grid.getView().getFooterPanel(true);
41259 this.footer.dataSource = this.grid.dataSource;
41260 this.footer = Roo.factory(this.footer, Roo);
41261 this.footer.render(ctr);
41271 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41272 getId : function(){
41273 return this.grid.id;
41277 * Returns the grid for this panel
41278 * @return {Roo.bootstrap.Table}
41280 getGrid : function(){
41284 setSize : function(width, height){
41285 if(!this.ignoreResize(width, height)){
41286 var grid = this.grid;
41287 var size = this.adjustForComponents(width, height);
41288 // tfoot is not a footer?
41291 var gridel = grid.getGridEl();
41292 gridel.setSize(size.width, size.height);
41294 var tbd = grid.getGridEl().select('tbody', true).first();
41295 var thd = grid.getGridEl().select('thead',true).first();
41296 var tbf= grid.getGridEl().select('tfoot', true).first();
41299 size.height -= tbf.getHeight();
41302 size.height -= thd.getHeight();
41305 tbd.setSize(size.width, size.height );
41306 // this is for the account management tab -seems to work there.
41307 var thd = grid.getGridEl().select('thead',true).first();
41309 // tbd.setSize(size.width, size.height - thd.getHeight());
41318 beforeSlide : function(){
41319 this.grid.getView().scroller.clip();
41322 afterSlide : function(){
41323 this.grid.getView().scroller.unclip();
41326 destroy : function(){
41327 this.grid.destroy();
41329 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
41334 * @class Roo.bootstrap.panel.Nest
41335 * @extends Roo.bootstrap.panel.Content
41337 * Create a new Panel, that can contain a layout.Border.
41340 * @param {Roo.BorderLayout} layout The layout for this panel
41341 * @param {String/Object} config A string to set only the title or a config object
41343 Roo.bootstrap.panel.Nest = function(config)
41345 // construct with only one argument..
41346 /* FIXME - implement nicer consturctors
41347 if (layout.layout) {
41349 layout = config.layout;
41350 delete config.layout;
41352 if (layout.xtype && !layout.getEl) {
41353 // then layout needs constructing..
41354 layout = Roo.factory(layout, Roo);
41358 config.el = config.layout.getEl();
41360 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41362 config.layout.monitorWindowResize = false; // turn off autosizing
41363 this.layout = config.layout;
41364 this.layout.getEl().addClass("roo-layout-nested-layout");
41365 this.layout.parent = this;
41372 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41374 setSize : function(width, height){
41375 if(!this.ignoreResize(width, height)){
41376 var size = this.adjustForComponents(width, height);
41377 var el = this.layout.getEl();
41378 if (size.height < 1) {
41379 el.setWidth(size.width);
41381 el.setSize(size.width, size.height);
41383 var touch = el.dom.offsetWidth;
41384 this.layout.layout();
41385 // ie requires a double layout on the first pass
41386 if(Roo.isIE && !this.initialized){
41387 this.initialized = true;
41388 this.layout.layout();
41393 // activate all subpanels if not currently active..
41395 setActiveState : function(active){
41396 this.active = active;
41397 this.setActiveClass(active);
41400 this.fireEvent("deactivate", this);
41404 this.fireEvent("activate", this);
41405 // not sure if this should happen before or after..
41406 if (!this.layout) {
41407 return; // should not happen..
41410 for (var r in this.layout.regions) {
41411 reg = this.layout.getRegion(r);
41412 if (reg.getActivePanel()) {
41413 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41414 reg.setActivePanel(reg.getActivePanel());
41417 if (!reg.panels.length) {
41420 reg.showPanel(reg.getPanel(0));
41429 * Returns the nested BorderLayout for this panel
41430 * @return {Roo.BorderLayout}
41432 getLayout : function(){
41433 return this.layout;
41437 * Adds a xtype elements to the layout of the nested panel
41441 xtype : 'ContentPanel',
41448 xtype : 'NestedLayoutPanel',
41454 items : [ ... list of content panels or nested layout panels.. ]
41458 * @param {Object} cfg Xtype definition of item to add.
41460 addxtype : function(cfg) {
41461 return this.layout.addxtype(cfg);
41466 * Ext JS Library 1.1.1
41467 * Copyright(c) 2006-2007, Ext JS, LLC.
41469 * Originally Released Under LGPL - original licence link has changed is not relivant.
41472 * <script type="text/javascript">
41475 * @class Roo.TabPanel
41476 * @extends Roo.util.Observable
41477 * A lightweight tab container.
41481 // basic tabs 1, built from existing content
41482 var tabs = new Roo.TabPanel("tabs1");
41483 tabs.addTab("script", "View Script");
41484 tabs.addTab("markup", "View Markup");
41485 tabs.activate("script");
41487 // more advanced tabs, built from javascript
41488 var jtabs = new Roo.TabPanel("jtabs");
41489 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41491 // set up the UpdateManager
41492 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41493 var updater = tab2.getUpdateManager();
41494 updater.setDefaultUrl("ajax1.htm");
41495 tab2.on('activate', updater.refresh, updater, true);
41497 // Use setUrl for Ajax loading
41498 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41499 tab3.setUrl("ajax2.htm", null, true);
41502 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41505 jtabs.activate("jtabs-1");
41508 * Create a new TabPanel.
41509 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41510 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41512 Roo.bootstrap.panel.Tabs = function(config){
41514 * The container element for this TabPanel.
41515 * @type Roo.Element
41517 this.el = Roo.get(config.el);
41520 if(typeof config == "boolean"){
41521 this.tabPosition = config ? "bottom" : "top";
41523 Roo.apply(this, config);
41527 if(this.tabPosition == "bottom"){
41528 // if tabs are at the bottom = create the body first.
41529 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41530 this.el.addClass("roo-tabs-bottom");
41532 // next create the tabs holders
41534 if (this.tabPosition == "west"){
41536 var reg = this.region; // fake it..
41538 if (!reg.mgr.parent) {
41541 reg = reg.mgr.parent.region;
41543 Roo.log("got nest?");
41545 if (reg.mgr.getRegion('west')) {
41546 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41547 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41548 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41549 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41550 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41558 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41559 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41560 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41561 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41566 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41569 // finally - if tabs are at the top, then create the body last..
41570 if(this.tabPosition != "bottom"){
41571 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41572 * @type Roo.Element
41574 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41575 this.el.addClass("roo-tabs-top");
41579 this.bodyEl.setStyle("position", "relative");
41581 this.active = null;
41582 this.activateDelegate = this.activate.createDelegate(this);
41587 * Fires when the active tab changes
41588 * @param {Roo.TabPanel} this
41589 * @param {Roo.TabPanelItem} activePanel The new active tab
41593 * @event beforetabchange
41594 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41595 * @param {Roo.TabPanel} this
41596 * @param {Object} e Set cancel to true on this object to cancel the tab change
41597 * @param {Roo.TabPanelItem} tab The tab being changed to
41599 "beforetabchange" : true
41602 Roo.EventManager.onWindowResize(this.onResize, this);
41603 this.cpad = this.el.getPadding("lr");
41604 this.hiddenCount = 0;
41607 // toolbar on the tabbar support...
41608 if (this.toolbar) {
41609 alert("no toolbar support yet");
41610 this.toolbar = false;
41612 var tcfg = this.toolbar;
41613 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41614 this.toolbar = new Roo.Toolbar(tcfg);
41615 if (Roo.isSafari) {
41616 var tbl = tcfg.container.child('table', true);
41617 tbl.setAttribute('width', '100%');
41625 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41628 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41630 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41632 tabPosition : "top",
41634 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41636 currentTabWidth : 0,
41638 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41642 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41646 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41648 preferredTabWidth : 175,
41650 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41652 resizeTabs : false,
41654 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41656 monitorResize : true,
41658 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41660 toolbar : false, // set by caller..
41662 region : false, /// set by caller
41664 disableTooltips : true, // not used yet...
41667 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41668 * @param {String} id The id of the div to use <b>or create</b>
41669 * @param {String} text The text for the tab
41670 * @param {String} content (optional) Content to put in the TabPanelItem body
41671 * @param {Boolean} closable (optional) True to create a close icon on the tab
41672 * @return {Roo.TabPanelItem} The created TabPanelItem
41674 addTab : function(id, text, content, closable, tpl)
41676 var item = new Roo.bootstrap.panel.TabItem({
41680 closable : closable,
41683 this.addTabItem(item);
41685 item.setContent(content);
41691 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41692 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41693 * @return {Roo.TabPanelItem}
41695 getTab : function(id){
41696 return this.items[id];
41700 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41701 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41703 hideTab : function(id){
41704 var t = this.items[id];
41707 this.hiddenCount++;
41708 this.autoSizeTabs();
41713 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41714 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41716 unhideTab : function(id){
41717 var t = this.items[id];
41719 t.setHidden(false);
41720 this.hiddenCount--;
41721 this.autoSizeTabs();
41726 * Adds an existing {@link Roo.TabPanelItem}.
41727 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41729 addTabItem : function(item)
41731 this.items[item.id] = item;
41732 this.items.push(item);
41733 this.autoSizeTabs();
41734 // if(this.resizeTabs){
41735 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41736 // this.autoSizeTabs();
41738 // item.autoSize();
41743 * Removes a {@link Roo.TabPanelItem}.
41744 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41746 removeTab : function(id){
41747 var items = this.items;
41748 var tab = items[id];
41749 if(!tab) { return; }
41750 var index = items.indexOf(tab);
41751 if(this.active == tab && items.length > 1){
41752 var newTab = this.getNextAvailable(index);
41757 this.stripEl.dom.removeChild(tab.pnode.dom);
41758 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41759 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41761 items.splice(index, 1);
41762 delete this.items[tab.id];
41763 tab.fireEvent("close", tab);
41764 tab.purgeListeners();
41765 this.autoSizeTabs();
41768 getNextAvailable : function(start){
41769 var items = this.items;
41771 // look for a next tab that will slide over to
41772 // replace the one being removed
41773 while(index < items.length){
41774 var item = items[++index];
41775 if(item && !item.isHidden()){
41779 // if one isn't found select the previous tab (on the left)
41782 var item = items[--index];
41783 if(item && !item.isHidden()){
41791 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41792 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41794 disableTab : function(id){
41795 var tab = this.items[id];
41796 if(tab && this.active != tab){
41802 * Enables a {@link Roo.TabPanelItem} that is disabled.
41803 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41805 enableTab : function(id){
41806 var tab = this.items[id];
41811 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41812 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41813 * @return {Roo.TabPanelItem} The TabPanelItem.
41815 activate : function(id)
41817 //Roo.log('activite:' + id);
41819 var tab = this.items[id];
41823 if(tab == this.active || tab.disabled){
41827 this.fireEvent("beforetabchange", this, e, tab);
41828 if(e.cancel !== true && !tab.disabled){
41830 this.active.hide();
41832 this.active = this.items[id];
41833 this.active.show();
41834 this.fireEvent("tabchange", this, this.active);
41840 * Gets the active {@link Roo.TabPanelItem}.
41841 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41843 getActiveTab : function(){
41844 return this.active;
41848 * Updates the tab body element to fit the height of the container element
41849 * for overflow scrolling
41850 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41852 syncHeight : function(targetHeight){
41853 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41854 var bm = this.bodyEl.getMargins();
41855 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41856 this.bodyEl.setHeight(newHeight);
41860 onResize : function(){
41861 if(this.monitorResize){
41862 this.autoSizeTabs();
41867 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41869 beginUpdate : function(){
41870 this.updating = true;
41874 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41876 endUpdate : function(){
41877 this.updating = false;
41878 this.autoSizeTabs();
41882 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41884 autoSizeTabs : function()
41886 var count = this.items.length;
41887 var vcount = count - this.hiddenCount;
41890 this.stripEl.hide();
41892 this.stripEl.show();
41895 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41900 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41901 var availWidth = Math.floor(w / vcount);
41902 var b = this.stripBody;
41903 if(b.getWidth() > w){
41904 var tabs = this.items;
41905 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41906 if(availWidth < this.minTabWidth){
41907 /*if(!this.sleft){ // incomplete scrolling code
41908 this.createScrollButtons();
41911 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41914 if(this.currentTabWidth < this.preferredTabWidth){
41915 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41921 * Returns the number of tabs in this TabPanel.
41924 getCount : function(){
41925 return this.items.length;
41929 * Resizes all the tabs to the passed width
41930 * @param {Number} The new width
41932 setTabWidth : function(width){
41933 this.currentTabWidth = width;
41934 for(var i = 0, len = this.items.length; i < len; i++) {
41935 if(!this.items[i].isHidden()) {
41936 this.items[i].setWidth(width);
41942 * Destroys this TabPanel
41943 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41945 destroy : function(removeEl){
41946 Roo.EventManager.removeResizeListener(this.onResize, this);
41947 for(var i = 0, len = this.items.length; i < len; i++){
41948 this.items[i].purgeListeners();
41950 if(removeEl === true){
41951 this.el.update("");
41956 createStrip : function(container)
41958 var strip = document.createElement("nav");
41959 strip.className = Roo.bootstrap.version == 4 ?
41960 "navbar-light bg-light" :
41961 "navbar navbar-default"; //"x-tabs-wrap";
41962 container.appendChild(strip);
41966 createStripList : function(strip)
41968 // div wrapper for retard IE
41969 // returns the "tr" element.
41970 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41971 //'<div class="x-tabs-strip-wrap">'+
41972 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41973 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41974 return strip.firstChild; //.firstChild.firstChild.firstChild;
41976 createBody : function(container)
41978 var body = document.createElement("div");
41979 Roo.id(body, "tab-body");
41980 //Roo.fly(body).addClass("x-tabs-body");
41981 Roo.fly(body).addClass("tab-content");
41982 container.appendChild(body);
41985 createItemBody :function(bodyEl, id){
41986 var body = Roo.getDom(id);
41988 body = document.createElement("div");
41991 //Roo.fly(body).addClass("x-tabs-item-body");
41992 Roo.fly(body).addClass("tab-pane");
41993 bodyEl.insertBefore(body, bodyEl.firstChild);
41997 createStripElements : function(stripEl, text, closable, tpl)
41999 var td = document.createElement("li"); // was td..
42000 td.className = 'nav-item';
42002 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
42005 stripEl.appendChild(td);
42007 td.className = "x-tabs-closable";
42008 if(!this.closeTpl){
42009 this.closeTpl = new Roo.Template(
42010 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
42011 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
42012 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
42015 var el = this.closeTpl.overwrite(td, {"text": text});
42016 var close = el.getElementsByTagName("div")[0];
42017 var inner = el.getElementsByTagName("em")[0];
42018 return {"el": el, "close": close, "inner": inner};
42021 // not sure what this is..
42022 // if(!this.tabTpl){
42023 //this.tabTpl = new Roo.Template(
42024 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
42025 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
42027 // this.tabTpl = new Roo.Template(
42028 // '<a href="#">' +
42029 // '<span unselectable="on"' +
42030 // (this.disableTooltips ? '' : ' title="{text}"') +
42031 // ' >{text}</span></a>'
42037 var template = tpl || this.tabTpl || false;
42040 template = new Roo.Template(
42041 Roo.bootstrap.version == 4 ?
42043 '<a class="nav-link" href="#" unselectable="on"' +
42044 (this.disableTooltips ? '' : ' title="{text}"') +
42047 '<a class="nav-link" href="#">' +
42048 '<span unselectable="on"' +
42049 (this.disableTooltips ? '' : ' title="{text}"') +
42050 ' >{text}</span></a>'
42055 switch (typeof(template)) {
42059 template = new Roo.Template(template);
42065 var el = template.overwrite(td, {"text": text});
42067 var inner = el.getElementsByTagName("span")[0];
42069 return {"el": el, "inner": inner};
42077 * @class Roo.TabPanelItem
42078 * @extends Roo.util.Observable
42079 * Represents an individual item (tab plus body) in a TabPanel.
42080 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42081 * @param {String} id The id of this TabPanelItem
42082 * @param {String} text The text for the tab of this TabPanelItem
42083 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42085 Roo.bootstrap.panel.TabItem = function(config){
42087 * The {@link Roo.TabPanel} this TabPanelItem belongs to
42088 * @type Roo.TabPanel
42090 this.tabPanel = config.panel;
42092 * The id for this TabPanelItem
42095 this.id = config.id;
42097 this.disabled = false;
42099 this.text = config.text;
42101 this.loaded = false;
42102 this.closable = config.closable;
42105 * The body element for this TabPanelItem.
42106 * @type Roo.Element
42108 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42109 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42110 this.bodyEl.setStyle("display", "block");
42111 this.bodyEl.setStyle("zoom", "1");
42112 //this.hideAction();
42114 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42116 this.el = Roo.get(els.el);
42117 this.inner = Roo.get(els.inner, true);
42118 this.textEl = Roo.bootstrap.version == 4 ?
42119 this.el : Roo.get(this.el.dom.firstChild, true);
42121 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42122 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42125 // this.el.on("mousedown", this.onTabMouseDown, this);
42126 this.el.on("click", this.onTabClick, this);
42128 if(config.closable){
42129 var c = Roo.get(els.close, true);
42130 c.dom.title = this.closeText;
42131 c.addClassOnOver("close-over");
42132 c.on("click", this.closeClick, this);
42138 * Fires when this tab becomes the active tab.
42139 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42140 * @param {Roo.TabPanelItem} this
42144 * @event beforeclose
42145 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42146 * @param {Roo.TabPanelItem} this
42147 * @param {Object} e Set cancel to true on this object to cancel the close.
42149 "beforeclose": true,
42152 * Fires when this tab is closed.
42153 * @param {Roo.TabPanelItem} this
42157 * @event deactivate
42158 * Fires when this tab is no longer the active tab.
42159 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42160 * @param {Roo.TabPanelItem} this
42162 "deactivate" : true
42164 this.hidden = false;
42166 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42169 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42171 purgeListeners : function(){
42172 Roo.util.Observable.prototype.purgeListeners.call(this);
42173 this.el.removeAllListeners();
42176 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42179 this.status_node.addClass("active");
42182 this.tabPanel.stripWrap.repaint();
42184 this.fireEvent("activate", this.tabPanel, this);
42188 * Returns true if this tab is the active tab.
42189 * @return {Boolean}
42191 isActive : function(){
42192 return this.tabPanel.getActiveTab() == this;
42196 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42199 this.status_node.removeClass("active");
42201 this.fireEvent("deactivate", this.tabPanel, this);
42204 hideAction : function(){
42205 this.bodyEl.hide();
42206 this.bodyEl.setStyle("position", "absolute");
42207 this.bodyEl.setLeft("-20000px");
42208 this.bodyEl.setTop("-20000px");
42211 showAction : function(){
42212 this.bodyEl.setStyle("position", "relative");
42213 this.bodyEl.setTop("");
42214 this.bodyEl.setLeft("");
42215 this.bodyEl.show();
42219 * Set the tooltip for the tab.
42220 * @param {String} tooltip The tab's tooltip
42222 setTooltip : function(text){
42223 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42224 this.textEl.dom.qtip = text;
42225 this.textEl.dom.removeAttribute('title');
42227 this.textEl.dom.title = text;
42231 onTabClick : function(e){
42232 e.preventDefault();
42233 this.tabPanel.activate(this.id);
42236 onTabMouseDown : function(e){
42237 e.preventDefault();
42238 this.tabPanel.activate(this.id);
42241 getWidth : function(){
42242 return this.inner.getWidth();
42245 setWidth : function(width){
42246 var iwidth = width - this.linode.getPadding("lr");
42247 this.inner.setWidth(iwidth);
42248 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42249 this.linode.setWidth(width);
42253 * Show or hide the tab
42254 * @param {Boolean} hidden True to hide or false to show.
42256 setHidden : function(hidden){
42257 this.hidden = hidden;
42258 this.linode.setStyle("display", hidden ? "none" : "");
42262 * Returns true if this tab is "hidden"
42263 * @return {Boolean}
42265 isHidden : function(){
42266 return this.hidden;
42270 * Returns the text for this tab
42273 getText : function(){
42277 autoSize : function(){
42278 //this.el.beginMeasure();
42279 this.textEl.setWidth(1);
42281 * #2804 [new] Tabs in Roojs
42282 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42284 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42285 //this.el.endMeasure();
42289 * Sets the text for the tab (Note: this also sets the tooltip text)
42290 * @param {String} text The tab's text and tooltip
42292 setText : function(text){
42294 this.textEl.update(text);
42295 this.setTooltip(text);
42296 //if(!this.tabPanel.resizeTabs){
42297 // this.autoSize();
42301 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42303 activate : function(){
42304 this.tabPanel.activate(this.id);
42308 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42310 disable : function(){
42311 if(this.tabPanel.active != this){
42312 this.disabled = true;
42313 this.status_node.addClass("disabled");
42318 * Enables this TabPanelItem if it was previously disabled.
42320 enable : function(){
42321 this.disabled = false;
42322 this.status_node.removeClass("disabled");
42326 * Sets the content for this TabPanelItem.
42327 * @param {String} content The content
42328 * @param {Boolean} loadScripts true to look for and load scripts
42330 setContent : function(content, loadScripts){
42331 this.bodyEl.update(content, loadScripts);
42335 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42336 * @return {Roo.UpdateManager} The UpdateManager
42338 getUpdateManager : function(){
42339 return this.bodyEl.getUpdateManager();
42343 * Set a URL to be used to load the content for this TabPanelItem.
42344 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42345 * @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)
42346 * @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)
42347 * @return {Roo.UpdateManager} The UpdateManager
42349 setUrl : function(url, params, loadOnce){
42350 if(this.refreshDelegate){
42351 this.un('activate', this.refreshDelegate);
42353 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42354 this.on("activate", this.refreshDelegate);
42355 return this.bodyEl.getUpdateManager();
42359 _handleRefresh : function(url, params, loadOnce){
42360 if(!loadOnce || !this.loaded){
42361 var updater = this.bodyEl.getUpdateManager();
42362 updater.update(url, params, this._setLoaded.createDelegate(this));
42367 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42368 * Will fail silently if the setUrl method has not been called.
42369 * This does not activate the panel, just updates its content.
42371 refresh : function(){
42372 if(this.refreshDelegate){
42373 this.loaded = false;
42374 this.refreshDelegate();
42379 _setLoaded : function(){
42380 this.loaded = true;
42384 closeClick : function(e){
42387 this.fireEvent("beforeclose", this, o);
42388 if(o.cancel !== true){
42389 this.tabPanel.removeTab(this.id);
42393 * The text displayed in the tooltip for the close icon.
42396 closeText : "Close this tab"
42399 * This script refer to:
42400 * Title: International Telephone Input
42401 * Author: Jack O'Connor
42402 * Code version: v12.1.12
42403 * Availability: https://github.com/jackocnr/intl-tel-input.git
42406 Roo.bootstrap.PhoneInputData = function() {
42409 "Afghanistan (افغانستان)",
42414 "Albania (Shqipëri)",
42419 "Algeria (الجزائر)",
42444 "Antigua and Barbuda",
42454 "Armenia (Հայաստան)",
42470 "Austria (Österreich)",
42475 "Azerbaijan (Azərbaycan)",
42485 "Bahrain (البحرين)",
42490 "Bangladesh (বাংলাদেশ)",
42500 "Belarus (Беларусь)",
42505 "Belgium (België)",
42535 "Bosnia and Herzegovina (Босна и Херцеговина)",
42550 "British Indian Ocean Territory",
42555 "British Virgin Islands",
42565 "Bulgaria (България)",
42575 "Burundi (Uburundi)",
42580 "Cambodia (កម្ពុជា)",
42585 "Cameroon (Cameroun)",
42594 ["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"]
42597 "Cape Verde (Kabu Verdi)",
42602 "Caribbean Netherlands",
42613 "Central African Republic (République centrafricaine)",
42633 "Christmas Island",
42639 "Cocos (Keeling) Islands",
42650 "Comoros (جزر القمر)",
42655 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42660 "Congo (Republic) (Congo-Brazzaville)",
42680 "Croatia (Hrvatska)",
42701 "Czech Republic (Česká republika)",
42706 "Denmark (Danmark)",
42721 "Dominican Republic (República Dominicana)",
42725 ["809", "829", "849"]
42743 "Equatorial Guinea (Guinea Ecuatorial)",
42763 "Falkland Islands (Islas Malvinas)",
42768 "Faroe Islands (Føroyar)",
42789 "French Guiana (Guyane française)",
42794 "French Polynesia (Polynésie française)",
42809 "Georgia (საქართველო)",
42814 "Germany (Deutschland)",
42834 "Greenland (Kalaallit Nunaat)",
42871 "Guinea-Bissau (Guiné Bissau)",
42896 "Hungary (Magyarország)",
42901 "Iceland (Ísland)",
42921 "Iraq (العراق)",
42937 "Israel (ישראל)",
42964 "Jordan (الأردن)",
42969 "Kazakhstan (Казахстан)",
42990 "Kuwait (الكويت)",
42995 "Kyrgyzstan (Кыргызстан)",
43005 "Latvia (Latvija)",
43010 "Lebanon (لبنان)",
43025 "Libya (ليبيا)",
43035 "Lithuania (Lietuva)",
43050 "Macedonia (FYROM) (Македонија)",
43055 "Madagascar (Madagasikara)",
43085 "Marshall Islands",
43095 "Mauritania (موريتانيا)",
43100 "Mauritius (Moris)",
43121 "Moldova (Republica Moldova)",
43131 "Mongolia (Монгол)",
43136 "Montenegro (Crna Gora)",
43146 "Morocco (المغرب)",
43152 "Mozambique (Moçambique)",
43157 "Myanmar (Burma) (မြန်မာ)",
43162 "Namibia (Namibië)",
43177 "Netherlands (Nederland)",
43182 "New Caledonia (Nouvelle-Calédonie)",
43217 "North Korea (조선 민주주의 인민 공화국)",
43222 "Northern Mariana Islands",
43238 "Pakistan (پاکستان)",
43248 "Palestine (فلسطين)",
43258 "Papua New Guinea",
43300 "Réunion (La Réunion)",
43306 "Romania (România)",
43322 "Saint Barthélemy",
43333 "Saint Kitts and Nevis",
43343 "Saint Martin (Saint-Martin (partie française))",
43349 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43354 "Saint Vincent and the Grenadines",
43369 "São Tomé and Príncipe (São Tomé e Príncipe)",
43374 "Saudi Arabia (المملكة العربية السعودية)",
43379 "Senegal (Sénégal)",
43409 "Slovakia (Slovensko)",
43414 "Slovenia (Slovenija)",
43424 "Somalia (Soomaaliya)",
43434 "South Korea (대한민국)",
43439 "South Sudan (جنوب السودان)",
43449 "Sri Lanka (ශ්රී ලංකාව)",
43454 "Sudan (السودان)",
43464 "Svalbard and Jan Mayen",
43475 "Sweden (Sverige)",
43480 "Switzerland (Schweiz)",
43485 "Syria (سوريا)",
43530 "Trinidad and Tobago",
43535 "Tunisia (تونس)",
43540 "Turkey (Türkiye)",
43550 "Turks and Caicos Islands",
43560 "U.S. Virgin Islands",
43570 "Ukraine (Україна)",
43575 "United Arab Emirates (الإمارات العربية المتحدة)",
43597 "Uzbekistan (Oʻzbekiston)",
43607 "Vatican City (Città del Vaticano)",
43618 "Vietnam (Việt Nam)",
43623 "Wallis and Futuna (Wallis-et-Futuna)",
43628 "Western Sahara (الصحراء الغربية)",
43634 "Yemen (اليمن)",
43658 * This script refer to:
43659 * Title: International Telephone Input
43660 * Author: Jack O'Connor
43661 * Code version: v12.1.12
43662 * Availability: https://github.com/jackocnr/intl-tel-input.git
43666 * @class Roo.bootstrap.PhoneInput
43667 * @extends Roo.bootstrap.TriggerField
43668 * An input with International dial-code selection
43670 * @cfg {String} defaultDialCode default '+852'
43671 * @cfg {Array} preferedCountries default []
43674 * Create a new PhoneInput.
43675 * @param {Object} config Configuration options
43678 Roo.bootstrap.PhoneInput = function(config) {
43679 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43682 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43684 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43686 listWidth: undefined,
43688 selectedClass: 'active',
43690 invalidClass : "has-warning",
43692 validClass: 'has-success',
43694 allowed: '0123456789',
43699 * @cfg {String} defaultDialCode The default dial code when initializing the input
43701 defaultDialCode: '+852',
43704 * @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
43706 preferedCountries: false,
43708 getAutoCreate : function()
43710 var data = Roo.bootstrap.PhoneInputData();
43711 var align = this.labelAlign || this.parentLabelAlign();
43714 this.allCountries = [];
43715 this.dialCodeMapping = [];
43717 for (var i = 0; i < data.length; i++) {
43719 this.allCountries[i] = {
43723 priority: c[3] || 0,
43724 areaCodes: c[4] || null
43726 this.dialCodeMapping[c[2]] = {
43729 priority: c[3] || 0,
43730 areaCodes: c[4] || null
43742 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43743 maxlength: this.max_length,
43744 cls : 'form-control tel-input',
43745 autocomplete: 'new-password'
43748 var hiddenInput = {
43751 cls: 'hidden-tel-input'
43755 hiddenInput.name = this.name;
43758 if (this.disabled) {
43759 input.disabled = true;
43762 var flag_container = {
43779 cls: this.hasFeedback ? 'has-feedback' : '',
43785 cls: 'dial-code-holder',
43792 cls: 'roo-select2-container input-group',
43799 if (this.fieldLabel.length) {
43802 tooltip: 'This field is required'
43808 cls: 'control-label',
43814 html: this.fieldLabel
43817 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43823 if(this.indicatorpos == 'right') {
43824 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43831 if(align == 'left') {
43839 if(this.labelWidth > 12){
43840 label.style = "width: " + this.labelWidth + 'px';
43842 if(this.labelWidth < 13 && this.labelmd == 0){
43843 this.labelmd = this.labelWidth;
43845 if(this.labellg > 0){
43846 label.cls += ' col-lg-' + this.labellg;
43847 input.cls += ' col-lg-' + (12 - this.labellg);
43849 if(this.labelmd > 0){
43850 label.cls += ' col-md-' + this.labelmd;
43851 container.cls += ' col-md-' + (12 - this.labelmd);
43853 if(this.labelsm > 0){
43854 label.cls += ' col-sm-' + this.labelsm;
43855 container.cls += ' col-sm-' + (12 - this.labelsm);
43857 if(this.labelxs > 0){
43858 label.cls += ' col-xs-' + this.labelxs;
43859 container.cls += ' col-xs-' + (12 - this.labelxs);
43869 var settings = this;
43871 ['xs','sm','md','lg'].map(function(size){
43872 if (settings[size]) {
43873 cfg.cls += ' col-' + size + '-' + settings[size];
43877 this.store = new Roo.data.Store({
43878 proxy : new Roo.data.MemoryProxy({}),
43879 reader : new Roo.data.JsonReader({
43890 'name' : 'dialCode',
43894 'name' : 'priority',
43898 'name' : 'areaCodes',
43905 if(!this.preferedCountries) {
43906 this.preferedCountries = [
43913 var p = this.preferedCountries.reverse();
43916 for (var i = 0; i < p.length; i++) {
43917 for (var j = 0; j < this.allCountries.length; j++) {
43918 if(this.allCountries[j].iso2 == p[i]) {
43919 var t = this.allCountries[j];
43920 this.allCountries.splice(j,1);
43921 this.allCountries.unshift(t);
43927 this.store.proxy.data = {
43929 data: this.allCountries
43935 initEvents : function()
43938 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43940 this.indicator = this.indicatorEl();
43941 this.flag = this.flagEl();
43942 this.dialCodeHolder = this.dialCodeHolderEl();
43944 this.trigger = this.el.select('div.flag-box',true).first();
43945 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43950 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43951 _this.list.setWidth(lw);
43954 this.list.on('mouseover', this.onViewOver, this);
43955 this.list.on('mousemove', this.onViewMove, this);
43956 this.inputEl().on("keyup", this.onKeyUp, this);
43957 this.inputEl().on("keypress", this.onKeyPress, this);
43959 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43961 this.view = new Roo.View(this.list, this.tpl, {
43962 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43965 this.view.on('click', this.onViewClick, this);
43966 this.setValue(this.defaultDialCode);
43969 onTriggerClick : function(e)
43971 Roo.log('trigger click');
43976 if(this.isExpanded()){
43978 this.hasFocus = false;
43980 this.store.load({});
43981 this.hasFocus = true;
43986 isExpanded : function()
43988 return this.list.isVisible();
43991 collapse : function()
43993 if(!this.isExpanded()){
43997 Roo.get(document).un('mousedown', this.collapseIf, this);
43998 Roo.get(document).un('mousewheel', this.collapseIf, this);
43999 this.fireEvent('collapse', this);
44003 expand : function()
44007 if(this.isExpanded() || !this.hasFocus){
44011 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
44012 this.list.setWidth(lw);
44015 this.restrictHeight();
44017 Roo.get(document).on('mousedown', this.collapseIf, this);
44018 Roo.get(document).on('mousewheel', this.collapseIf, this);
44020 this.fireEvent('expand', this);
44023 restrictHeight : function()
44025 this.list.alignTo(this.inputEl(), this.listAlign);
44026 this.list.alignTo(this.inputEl(), this.listAlign);
44029 onViewOver : function(e, t)
44031 if(this.inKeyMode){
44034 var item = this.view.findItemFromChild(t);
44037 var index = this.view.indexOf(item);
44038 this.select(index, false);
44043 onViewClick : function(view, doFocus, el, e)
44045 var index = this.view.getSelectedIndexes()[0];
44047 var r = this.store.getAt(index);
44050 this.onSelect(r, index);
44052 if(doFocus !== false && !this.blockFocus){
44053 this.inputEl().focus();
44057 onViewMove : function(e, t)
44059 this.inKeyMode = false;
44062 select : function(index, scrollIntoView)
44064 this.selectedIndex = index;
44065 this.view.select(index);
44066 if(scrollIntoView !== false){
44067 var el = this.view.getNode(index);
44069 this.list.scrollChildIntoView(el, false);
44074 createList : function()
44076 this.list = Roo.get(document.body).createChild({
44078 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44079 style: 'display:none'
44082 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44085 collapseIf : function(e)
44087 var in_combo = e.within(this.el);
44088 var in_list = e.within(this.list);
44089 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44091 if (in_combo || in_list || is_list) {
44097 onSelect : function(record, index)
44099 if(this.fireEvent('beforeselect', this, record, index) !== false){
44101 this.setFlagClass(record.data.iso2);
44102 this.setDialCode(record.data.dialCode);
44103 this.hasFocus = false;
44105 this.fireEvent('select', this, record, index);
44109 flagEl : function()
44111 var flag = this.el.select('div.flag',true).first();
44118 dialCodeHolderEl : function()
44120 var d = this.el.select('input.dial-code-holder',true).first();
44127 setDialCode : function(v)
44129 this.dialCodeHolder.dom.value = '+'+v;
44132 setFlagClass : function(n)
44134 this.flag.dom.className = 'flag '+n;
44137 getValue : function()
44139 var v = this.inputEl().getValue();
44140 if(this.dialCodeHolder) {
44141 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44146 setValue : function(v)
44148 var d = this.getDialCode(v);
44150 //invalid dial code
44151 if(v.length == 0 || !d || d.length == 0) {
44153 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44154 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44160 this.setFlagClass(this.dialCodeMapping[d].iso2);
44161 this.setDialCode(d);
44162 this.inputEl().dom.value = v.replace('+'+d,'');
44163 this.hiddenEl().dom.value = this.getValue();
44168 getDialCode : function(v)
44172 if (v.length == 0) {
44173 return this.dialCodeHolder.dom.value;
44177 if (v.charAt(0) != "+") {
44180 var numericChars = "";
44181 for (var i = 1; i < v.length; i++) {
44182 var c = v.charAt(i);
44185 if (this.dialCodeMapping[numericChars]) {
44186 dialCode = v.substr(1, i);
44188 if (numericChars.length == 4) {
44198 this.setValue(this.defaultDialCode);
44202 hiddenEl : function()
44204 return this.el.select('input.hidden-tel-input',true).first();
44207 // after setting val
44208 onKeyUp : function(e){
44209 this.setValue(this.getValue());
44212 onKeyPress : function(e){
44213 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44220 * @class Roo.bootstrap.MoneyField
44221 * @extends Roo.bootstrap.ComboBox
44222 * Bootstrap MoneyField class
44225 * Create a new MoneyField.
44226 * @param {Object} config Configuration options
44229 Roo.bootstrap.MoneyField = function(config) {
44231 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44235 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44238 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44240 allowDecimals : true,
44242 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44244 decimalSeparator : ".",
44246 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44248 decimalPrecision : 0,
44250 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44252 allowNegative : true,
44254 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44258 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44260 minValue : Number.NEGATIVE_INFINITY,
44262 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44264 maxValue : Number.MAX_VALUE,
44266 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44268 minText : "The minimum value for this field is {0}",
44270 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44272 maxText : "The maximum value for this field is {0}",
44274 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
44275 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44277 nanText : "{0} is not a valid number",
44279 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44283 * @cfg {String} defaults currency of the MoneyField
44284 * value should be in lkey
44286 defaultCurrency : false,
44288 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44290 thousandsDelimiter : false,
44292 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44303 getAutoCreate : function()
44305 var align = this.labelAlign || this.parentLabelAlign();
44317 cls : 'form-control roo-money-amount-input',
44318 autocomplete: 'new-password'
44321 var hiddenInput = {
44325 cls: 'hidden-number-input'
44328 if(this.max_length) {
44329 input.maxlength = this.max_length;
44333 hiddenInput.name = this.name;
44336 if (this.disabled) {
44337 input.disabled = true;
44340 var clg = 12 - this.inputlg;
44341 var cmd = 12 - this.inputmd;
44342 var csm = 12 - this.inputsm;
44343 var cxs = 12 - this.inputxs;
44347 cls : 'row roo-money-field',
44351 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44355 cls: 'roo-select2-container input-group',
44359 cls : 'form-control roo-money-currency-input',
44360 autocomplete: 'new-password',
44362 name : this.currencyName
44366 cls : 'input-group-addon',
44380 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44384 cls: this.hasFeedback ? 'has-feedback' : '',
44395 if (this.fieldLabel.length) {
44398 tooltip: 'This field is required'
44404 cls: 'control-label',
44410 html: this.fieldLabel
44413 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44419 if(this.indicatorpos == 'right') {
44420 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44427 if(align == 'left') {
44435 if(this.labelWidth > 12){
44436 label.style = "width: " + this.labelWidth + 'px';
44438 if(this.labelWidth < 13 && this.labelmd == 0){
44439 this.labelmd = this.labelWidth;
44441 if(this.labellg > 0){
44442 label.cls += ' col-lg-' + this.labellg;
44443 input.cls += ' col-lg-' + (12 - this.labellg);
44445 if(this.labelmd > 0){
44446 label.cls += ' col-md-' + this.labelmd;
44447 container.cls += ' col-md-' + (12 - this.labelmd);
44449 if(this.labelsm > 0){
44450 label.cls += ' col-sm-' + this.labelsm;
44451 container.cls += ' col-sm-' + (12 - this.labelsm);
44453 if(this.labelxs > 0){
44454 label.cls += ' col-xs-' + this.labelxs;
44455 container.cls += ' col-xs-' + (12 - this.labelxs);
44466 var settings = this;
44468 ['xs','sm','md','lg'].map(function(size){
44469 if (settings[size]) {
44470 cfg.cls += ' col-' + size + '-' + settings[size];
44477 initEvents : function()
44479 this.indicator = this.indicatorEl();
44481 this.initCurrencyEvent();
44483 this.initNumberEvent();
44486 initCurrencyEvent : function()
44489 throw "can not find store for combo";
44492 this.store = Roo.factory(this.store, Roo.data);
44493 this.store.parent = this;
44497 this.triggerEl = this.el.select('.input-group-addon', true).first();
44499 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44504 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44505 _this.list.setWidth(lw);
44508 this.list.on('mouseover', this.onViewOver, this);
44509 this.list.on('mousemove', this.onViewMove, this);
44510 this.list.on('scroll', this.onViewScroll, this);
44513 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44516 this.view = new Roo.View(this.list, this.tpl, {
44517 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44520 this.view.on('click', this.onViewClick, this);
44522 this.store.on('beforeload', this.onBeforeLoad, this);
44523 this.store.on('load', this.onLoad, this);
44524 this.store.on('loadexception', this.onLoadException, this);
44526 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44527 "up" : function(e){
44528 this.inKeyMode = true;
44532 "down" : function(e){
44533 if(!this.isExpanded()){
44534 this.onTriggerClick();
44536 this.inKeyMode = true;
44541 "enter" : function(e){
44544 if(this.fireEvent("specialkey", this, e)){
44545 this.onViewClick(false);
44551 "esc" : function(e){
44555 "tab" : function(e){
44558 if(this.fireEvent("specialkey", this, e)){
44559 this.onViewClick(false);
44567 doRelay : function(foo, bar, hname){
44568 if(hname == 'down' || this.scope.isExpanded()){
44569 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44577 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44581 initNumberEvent : function(e)
44583 this.inputEl().on("keydown" , this.fireKey, this);
44584 this.inputEl().on("focus", this.onFocus, this);
44585 this.inputEl().on("blur", this.onBlur, this);
44587 this.inputEl().relayEvent('keyup', this);
44589 if(this.indicator){
44590 this.indicator.addClass('invisible');
44593 this.originalValue = this.getValue();
44595 if(this.validationEvent == 'keyup'){
44596 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44597 this.inputEl().on('keyup', this.filterValidation, this);
44599 else if(this.validationEvent !== false){
44600 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44603 if(this.selectOnFocus){
44604 this.on("focus", this.preFocus, this);
44607 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44608 this.inputEl().on("keypress", this.filterKeys, this);
44610 this.inputEl().relayEvent('keypress', this);
44613 var allowed = "0123456789";
44615 if(this.allowDecimals){
44616 allowed += this.decimalSeparator;
44619 if(this.allowNegative){
44623 if(this.thousandsDelimiter) {
44627 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44629 var keyPress = function(e){
44631 var k = e.getKey();
44633 var c = e.getCharCode();
44636 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44637 allowed.indexOf(String.fromCharCode(c)) === -1
44643 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44647 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44652 this.inputEl().on("keypress", keyPress, this);
44656 onTriggerClick : function(e)
44663 this.loadNext = false;
44665 if(this.isExpanded()){
44670 this.hasFocus = true;
44672 if(this.triggerAction == 'all') {
44673 this.doQuery(this.allQuery, true);
44677 this.doQuery(this.getRawValue());
44680 getCurrency : function()
44682 var v = this.currencyEl().getValue();
44687 restrictHeight : function()
44689 this.list.alignTo(this.currencyEl(), this.listAlign);
44690 this.list.alignTo(this.currencyEl(), this.listAlign);
44693 onViewClick : function(view, doFocus, el, e)
44695 var index = this.view.getSelectedIndexes()[0];
44697 var r = this.store.getAt(index);
44700 this.onSelect(r, index);
44704 onSelect : function(record, index){
44706 if(this.fireEvent('beforeselect', this, record, index) !== false){
44708 this.setFromCurrencyData(index > -1 ? record.data : false);
44712 this.fireEvent('select', this, record, index);
44716 setFromCurrencyData : function(o)
44720 this.lastCurrency = o;
44722 if (this.currencyField) {
44723 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44725 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44728 this.lastSelectionText = currency;
44730 //setting default currency
44731 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44732 this.setCurrency(this.defaultCurrency);
44736 this.setCurrency(currency);
44739 setFromData : function(o)
44743 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44745 this.setFromCurrencyData(c);
44750 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44752 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44755 this.setValue(value);
44759 setCurrency : function(v)
44761 this.currencyValue = v;
44764 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44769 setValue : function(v)
44771 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44777 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44779 this.inputEl().dom.value = (v == '') ? '' :
44780 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44782 if(!this.allowZero && v === '0') {
44783 this.hiddenEl().dom.value = '';
44784 this.inputEl().dom.value = '';
44791 getRawValue : function()
44793 var v = this.inputEl().getValue();
44798 getValue : function()
44800 return this.fixPrecision(this.parseValue(this.getRawValue()));
44803 parseValue : function(value)
44805 if(this.thousandsDelimiter) {
44807 r = new RegExp(",", "g");
44808 value = value.replace(r, "");
44811 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44812 return isNaN(value) ? '' : value;
44816 fixPrecision : function(value)
44818 if(this.thousandsDelimiter) {
44820 r = new RegExp(",", "g");
44821 value = value.replace(r, "");
44824 var nan = isNaN(value);
44826 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44827 return nan ? '' : value;
44829 return parseFloat(value).toFixed(this.decimalPrecision);
44832 decimalPrecisionFcn : function(v)
44834 return Math.floor(v);
44837 validateValue : function(value)
44839 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44843 var num = this.parseValue(value);
44846 this.markInvalid(String.format(this.nanText, value));
44850 if(num < this.minValue){
44851 this.markInvalid(String.format(this.minText, this.minValue));
44855 if(num > this.maxValue){
44856 this.markInvalid(String.format(this.maxText, this.maxValue));
44863 validate : function()
44865 if(this.disabled || this.allowBlank){
44870 var currency = this.getCurrency();
44872 if(this.validateValue(this.getRawValue()) && currency.length){
44877 this.markInvalid();
44881 getName: function()
44886 beforeBlur : function()
44892 var v = this.parseValue(this.getRawValue());
44899 onBlur : function()
44903 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44904 //this.el.removeClass(this.focusClass);
44907 this.hasFocus = false;
44909 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44913 var v = this.getValue();
44915 if(String(v) !== String(this.startValue)){
44916 this.fireEvent('change', this, v, this.startValue);
44919 this.fireEvent("blur", this);
44922 inputEl : function()
44924 return this.el.select('.roo-money-amount-input', true).first();
44927 currencyEl : function()
44929 return this.el.select('.roo-money-currency-input', true).first();
44932 hiddenEl : function()
44934 return this.el.select('input.hidden-number-input',true).first();
44938 * @class Roo.bootstrap.BezierSignature
44939 * @extends Roo.bootstrap.Component
44940 * Bootstrap BezierSignature class
44941 * This script refer to:
44942 * Title: Signature Pad
44944 * Availability: https://github.com/szimek/signature_pad
44947 * Create a new BezierSignature
44948 * @param {Object} config The config object
44951 Roo.bootstrap.BezierSignature = function(config){
44952 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44958 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44965 mouse_btn_down: true,
44968 * @cfg {int} canvas height
44970 canvas_height: '200px',
44973 * @cfg {float|function} Radius of a single dot.
44978 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44983 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44988 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44993 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44998 * @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.
45000 bg_color: 'rgba(0, 0, 0, 0)',
45003 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
45005 dot_color: 'black',
45008 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
45010 velocity_filter_weight: 0.7,
45013 * @cfg {function} Callback when stroke begin.
45018 * @cfg {function} Callback when stroke end.
45022 getAutoCreate : function()
45024 var cls = 'roo-signature column';
45027 cls += ' ' + this.cls;
45037 for(var i = 0; i < col_sizes.length; i++) {
45038 if(this[col_sizes[i]]) {
45039 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
45049 cls: 'roo-signature-body',
45053 cls: 'roo-signature-body-canvas',
45054 height: this.canvas_height,
45055 width: this.canvas_width
45062 style: 'display: none'
45070 initEvents: function()
45072 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45074 var canvas = this.canvasEl();
45076 // mouse && touch event swapping...
45077 canvas.dom.style.touchAction = 'none';
45078 canvas.dom.style.msTouchAction = 'none';
45080 this.mouse_btn_down = false;
45081 canvas.on('mousedown', this._handleMouseDown, this);
45082 canvas.on('mousemove', this._handleMouseMove, this);
45083 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45085 if (window.PointerEvent) {
45086 canvas.on('pointerdown', this._handleMouseDown, this);
45087 canvas.on('pointermove', this._handleMouseMove, this);
45088 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45091 if ('ontouchstart' in window) {
45092 canvas.on('touchstart', this._handleTouchStart, this);
45093 canvas.on('touchmove', this._handleTouchMove, this);
45094 canvas.on('touchend', this._handleTouchEnd, this);
45097 Roo.EventManager.onWindowResize(this.resize, this, true);
45099 // file input event
45100 this.fileEl().on('change', this.uploadImage, this);
45107 resize: function(){
45109 var canvas = this.canvasEl().dom;
45110 var ctx = this.canvasElCtx();
45111 var img_data = false;
45113 if(canvas.width > 0) {
45114 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45116 // setting canvas width will clean img data
45119 var style = window.getComputedStyle ?
45120 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45122 var padding_left = parseInt(style.paddingLeft) || 0;
45123 var padding_right = parseInt(style.paddingRight) || 0;
45125 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45128 ctx.putImageData(img_data, 0, 0);
45132 _handleMouseDown: function(e)
45134 if (e.browserEvent.which === 1) {
45135 this.mouse_btn_down = true;
45136 this.strokeBegin(e);
45140 _handleMouseMove: function (e)
45142 if (this.mouse_btn_down) {
45143 this.strokeMoveUpdate(e);
45147 _handleMouseUp: function (e)
45149 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45150 this.mouse_btn_down = false;
45155 _handleTouchStart: function (e) {
45157 e.preventDefault();
45158 if (e.browserEvent.targetTouches.length === 1) {
45159 // var touch = e.browserEvent.changedTouches[0];
45160 // this.strokeBegin(touch);
45162 this.strokeBegin(e); // assume e catching the correct xy...
45166 _handleTouchMove: function (e) {
45167 e.preventDefault();
45168 // var touch = event.targetTouches[0];
45169 // _this._strokeMoveUpdate(touch);
45170 this.strokeMoveUpdate(e);
45173 _handleTouchEnd: function (e) {
45174 var wasCanvasTouched = e.target === this.canvasEl().dom;
45175 if (wasCanvasTouched) {
45176 e.preventDefault();
45177 // var touch = event.changedTouches[0];
45178 // _this._strokeEnd(touch);
45183 reset: function () {
45184 this._lastPoints = [];
45185 this._lastVelocity = 0;
45186 this._lastWidth = (this.min_width + this.max_width) / 2;
45187 this.canvasElCtx().fillStyle = this.dot_color;
45190 strokeMoveUpdate: function(e)
45192 this.strokeUpdate(e);
45194 if (this.throttle) {
45195 this.throttleStroke(this.strokeUpdate, this.throttle);
45198 this.strokeUpdate(e);
45202 strokeBegin: function(e)
45204 var newPointGroup = {
45205 color: this.dot_color,
45209 if (typeof this.onBegin === 'function') {
45213 this.curve_data.push(newPointGroup);
45215 this.strokeUpdate(e);
45218 strokeUpdate: function(e)
45220 var rect = this.canvasEl().dom.getBoundingClientRect();
45221 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45222 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45223 var lastPoints = lastPointGroup.points;
45224 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45225 var isLastPointTooClose = lastPoint
45226 ? point.distanceTo(lastPoint) <= this.min_distance
45228 var color = lastPointGroup.color;
45229 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45230 var curve = this.addPoint(point);
45232 this.drawDot({color: color, point: point});
45235 this.drawCurve({color: color, curve: curve});
45245 strokeEnd: function(e)
45247 this.strokeUpdate(e);
45248 if (typeof this.onEnd === 'function') {
45253 addPoint: function (point) {
45254 var _lastPoints = this._lastPoints;
45255 _lastPoints.push(point);
45256 if (_lastPoints.length > 2) {
45257 if (_lastPoints.length === 3) {
45258 _lastPoints.unshift(_lastPoints[0]);
45260 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45261 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45262 _lastPoints.shift();
45268 calculateCurveWidths: function (startPoint, endPoint) {
45269 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45270 (1 - this.velocity_filter_weight) * this._lastVelocity;
45272 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45275 start: this._lastWidth
45278 this._lastVelocity = velocity;
45279 this._lastWidth = newWidth;
45283 drawDot: function (_a) {
45284 var color = _a.color, point = _a.point;
45285 var ctx = this.canvasElCtx();
45286 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45288 this.drawCurveSegment(point.x, point.y, width);
45290 ctx.fillStyle = color;
45294 drawCurve: function (_a) {
45295 var color = _a.color, curve = _a.curve;
45296 var ctx = this.canvasElCtx();
45297 var widthDelta = curve.endWidth - curve.startWidth;
45298 var drawSteps = Math.floor(curve.length()) * 2;
45300 ctx.fillStyle = color;
45301 for (var i = 0; i < drawSteps; i += 1) {
45302 var t = i / drawSteps;
45308 var x = uuu * curve.startPoint.x;
45309 x += 3 * uu * t * curve.control1.x;
45310 x += 3 * u * tt * curve.control2.x;
45311 x += ttt * curve.endPoint.x;
45312 var y = uuu * curve.startPoint.y;
45313 y += 3 * uu * t * curve.control1.y;
45314 y += 3 * u * tt * curve.control2.y;
45315 y += ttt * curve.endPoint.y;
45316 var width = curve.startWidth + ttt * widthDelta;
45317 this.drawCurveSegment(x, y, width);
45323 drawCurveSegment: function (x, y, width) {
45324 var ctx = this.canvasElCtx();
45326 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45327 this.is_empty = false;
45332 var ctx = this.canvasElCtx();
45333 var canvas = this.canvasEl().dom;
45334 ctx.fillStyle = this.bg_color;
45335 ctx.clearRect(0, 0, canvas.width, canvas.height);
45336 ctx.fillRect(0, 0, canvas.width, canvas.height);
45337 this.curve_data = [];
45339 this.is_empty = true;
45344 return this.el.select('input',true).first();
45347 canvasEl: function()
45349 return this.el.select('canvas',true).first();
45352 canvasElCtx: function()
45354 return this.el.select('canvas',true).first().dom.getContext('2d');
45357 getImage: function(type)
45359 if(this.is_empty) {
45364 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45367 drawFromImage: function(img_src)
45369 var img = new Image();
45371 img.onload = function(){
45372 this.canvasElCtx().drawImage(img, 0, 0);
45377 this.is_empty = false;
45380 selectImage: function()
45382 this.fileEl().dom.click();
45385 uploadImage: function(e)
45387 var reader = new FileReader();
45389 reader.onload = function(e){
45390 var img = new Image();
45391 img.onload = function(){
45393 this.canvasElCtx().drawImage(img, 0, 0);
45395 img.src = e.target.result;
45398 reader.readAsDataURL(e.target.files[0]);
45401 // Bezier Point Constructor
45402 Point: (function () {
45403 function Point(x, y, time) {
45406 this.time = time || Date.now();
45408 Point.prototype.distanceTo = function (start) {
45409 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45411 Point.prototype.equals = function (other) {
45412 return this.x === other.x && this.y === other.y && this.time === other.time;
45414 Point.prototype.velocityFrom = function (start) {
45415 return this.time !== start.time
45416 ? this.distanceTo(start) / (this.time - start.time)
45423 // Bezier Constructor
45424 Bezier: (function () {
45425 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45426 this.startPoint = startPoint;
45427 this.control2 = control2;
45428 this.control1 = control1;
45429 this.endPoint = endPoint;
45430 this.startWidth = startWidth;
45431 this.endWidth = endWidth;
45433 Bezier.fromPoints = function (points, widths, scope) {
45434 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45435 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45436 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45438 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45439 var dx1 = s1.x - s2.x;
45440 var dy1 = s1.y - s2.y;
45441 var dx2 = s2.x - s3.x;
45442 var dy2 = s2.y - s3.y;
45443 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45444 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45445 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45446 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45447 var dxm = m1.x - m2.x;
45448 var dym = m1.y - m2.y;
45449 var k = l2 / (l1 + l2);
45450 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45451 var tx = s2.x - cm.x;
45452 var ty = s2.y - cm.y;
45454 c1: new scope.Point(m1.x + tx, m1.y + ty),
45455 c2: new scope.Point(m2.x + tx, m2.y + ty)
45458 Bezier.prototype.length = function () {
45463 for (var i = 0; i <= steps; i += 1) {
45465 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45466 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45468 var xdiff = cx - px;
45469 var ydiff = cy - py;
45470 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45477 Bezier.prototype.point = function (t, start, c1, c2, end) {
45478 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45479 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45480 + (3.0 * c2 * (1.0 - t) * t * t)
45481 + (end * t * t * t);
45486 throttleStroke: function(fn, wait) {
45487 if (wait === void 0) { wait = 250; }
45489 var timeout = null;
45493 var later = function () {
45494 previous = Date.now();
45496 result = fn.apply(storedContext, storedArgs);
45498 storedContext = null;
45502 return function wrapper() {
45504 for (var _i = 0; _i < arguments.length; _i++) {
45505 args[_i] = arguments[_i];
45507 var now = Date.now();
45508 var remaining = wait - (now - previous);
45509 storedContext = this;
45511 if (remaining <= 0 || remaining > wait) {
45513 clearTimeout(timeout);
45517 result = fn.apply(storedContext, storedArgs);
45519 storedContext = null;
45523 else if (!timeout) {
45524 timeout = window.setTimeout(later, remaining);