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;
17 })(); Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18 Roo.bootstrap.nav = {};
20 Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};
22 Roo.namespace('Roo.bootstrap.form.HtmlEditorToolbar');
25 * Ext JS Library 1.1.1
26 * Copyright(c) 2006-2007, Ext JS, LLC.
28 * Originally Released Under LGPL - original licence link has changed is not relivant.
31 * <script type="text/javascript">
37 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
38 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
39 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
42 * @param {Object} config The config object
44 Roo.Shadow = function(config){
45 Roo.apply(this, config);
46 if(typeof this.mode != "string"){
47 this.mode = this.defaultMode;
49 var o = this.offset, a = {h: 0};
50 var rad = Math.floor(this.offset/2);
51 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
57 a.l -= this.offset + rad;
58 a.t -= this.offset + rad;
69 a.l -= (this.offset - rad);
70 a.t -= this.offset + rad;
72 a.w -= (this.offset - rad)*2;
83 a.l -= (this.offset - rad);
84 a.t -= (this.offset - rad);
86 a.w -= (this.offset + rad + 1);
87 a.h -= (this.offset + rad);
96 Roo.Shadow.prototype = {
99 * The shadow display mode. Supports the following options:<br />
100 * sides: Shadow displays on both sides and bottom only<br />
101 * frame: Shadow displays equally on all four sides<br />
102 * drop: Traditional bottom-right drop shadow (default)
106 * @cfg {String} offset
107 * The number of pixels to offset the shadow from the element (defaults to 4)
115 * Displays the shadow under the target element
116 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
118 show : function(target){
119 target = Roo.get(target);
121 this.el = Roo.Shadow.Pool.pull();
122 if(this.el.dom.nextSibling != target.dom){
123 this.el.insertBefore(target);
126 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
128 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
131 target.getLeft(true),
136 this.el.dom.style.display = "block";
140 * Returns true if the shadow is visible, else false
142 isVisible : function(){
143 return this.el ? true : false;
147 * Direct alignment when values are already available. Show must be called at least once before
148 * calling this method to ensure it is initialized.
149 * @param {Number} left The target element left position
150 * @param {Number} top The target element top position
151 * @param {Number} width The target element width
152 * @param {Number} height The target element height
154 realign : function(l, t, w, h){
158 var a = this.adjusts, d = this.el.dom, s = d.style;
160 s.left = (l+a.l)+"px";
161 s.top = (t+a.t)+"px";
162 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
164 if(s.width != sws || s.height != shs){
168 var cn = d.childNodes;
169 var sww = Math.max(0, (sw-12))+"px";
170 cn[0].childNodes[1].style.width = sww;
171 cn[1].childNodes[1].style.width = sww;
172 cn[2].childNodes[1].style.width = sww;
173 cn[1].style.height = Math.max(0, (sh-12))+"px";
183 this.el.dom.style.display = "none";
184 Roo.Shadow.Pool.push(this.el);
190 * Adjust the z-index of this shadow
191 * @param {Number} zindex The new z-index
193 setZIndex : function(z){
196 this.el.setStyle("z-index", z);
201 // Private utility class that manages the internal Shadow cache
202 Roo.Shadow.Pool = function(){
204 var markup = Roo.isIE ?
205 '<div class="x-ie-shadow"></div>' :
206 '<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>';
211 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
212 sh.autoBoxAdjust = false;
224 * base class for bootstrap elements.
228 Roo.bootstrap = Roo.bootstrap || {};
230 * @class Roo.bootstrap.Component
231 * @extends Roo.Component
233 * @children Roo.bootstrap.Component
234 * Bootstrap Component base class
235 * @cfg {String} cls css class
236 * @cfg {String} style any extra css
237 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
238 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
239 * @cfg {string} dataId cutomer id
240 * @cfg {string} name Specifies name attribute
241 * @cfg {string} tooltip Text for the tooltip
242 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
243 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
246 * Do not use directly - it does not do anything..
247 * @param {Object} config The config object
252 Roo.bootstrap.Component = function(config){
253 Roo.bootstrap.Component.superclass.constructor.call(this, config);
257 * @event childrenrendered
258 * Fires when the children have been rendered..
259 * @param {Roo.bootstrap.Component} this
261 "childrenrendered" : true
270 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
273 allowDomMove : false, // to stop relocations in parent onRender...
283 * Initialize Events for the element
285 initEvents : function() { },
291 can_build_overlaid : true,
293 container_method : false,
300 // returns the parent component..
301 return Roo.ComponentMgr.get(this.parentId)
307 onRender : function(ct, position)
309 // Roo.log("Call onRender: " + this.xtype);
311 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
314 if (this.el.attr('xtype')) {
315 this.el.attr('xtypex', this.el.attr('xtype'));
316 this.el.dom.removeAttribute('xtype');
326 var cfg = Roo.apply({}, this.getAutoCreate());
328 cfg.id = this.id || Roo.id();
330 // fill in the extra attributes
331 if (this.xattr && typeof(this.xattr) =='object') {
332 for (var i in this.xattr) {
333 cfg[i] = this.xattr[i];
338 cfg.dataId = this.dataId;
342 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
345 if (this.style) { // fixme needs to support more complex style data.
346 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
350 cfg.name = this.name;
353 this.el = ct.createChild(cfg, position);
356 this.tooltipEl().attr('tooltip', this.tooltip);
359 if(this.tabIndex !== undefined){
360 this.el.dom.setAttribute('tabIndex', this.tabIndex);
367 * Fetch the element to add children to
368 * @return {Roo.Element} defaults to this.el
370 getChildContainer : function()
374 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
376 return Roo.get(document.body);
380 * Fetch the element to display the tooltip on.
381 * @return {Roo.Element} defaults to this.el
383 tooltipEl : function()
388 addxtype : function(tree,cntr)
392 cn = Roo.factory(tree);
393 //Roo.log(['addxtype', cn]);
395 cn.parentType = this.xtype; //??
396 cn.parentId = this.id;
398 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
399 if (typeof(cn.container_method) == 'string') {
400 cntr = cn.container_method;
404 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
406 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
408 var build_from_html = Roo.XComponent.build_from_html;
410 var is_body = (tree.xtype == 'Body') ;
412 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
414 var self_cntr_el = Roo.get(this[cntr](false));
416 // do not try and build conditional elements
417 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
421 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
422 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
423 return this.addxtypeChild(tree,cntr, is_body);
426 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
429 return this.addxtypeChild(Roo.apply({}, tree),cntr);
432 Roo.log('skipping render');
438 if (!build_from_html) {
442 // this i think handles overlaying multiple children of the same type
443 // with the sam eelement.. - which might be buggy..
445 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
451 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
455 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
462 addxtypeChild : function (tree, cntr, is_body)
464 Roo.debug && Roo.log('addxtypeChild:' + cntr);
466 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
469 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
470 (typeof(tree['flexy:foreach']) != 'undefined');
474 skip_children = false;
475 // render the element if it's not BODY.
478 // if parent was disabled, then do not try and create the children..
479 if(!this[cntr](true)){
484 cn = Roo.factory(tree);
486 cn.parentType = this.xtype; //??
487 cn.parentId = this.id;
489 var build_from_html = Roo.XComponent.build_from_html;
492 // does the container contain child eleemnts with 'xtype' attributes.
493 // that match this xtype..
494 // note - when we render we create these as well..
495 // so we should check to see if body has xtype set.
496 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
498 var self_cntr_el = Roo.get(this[cntr](false));
499 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
501 //Roo.log(Roo.XComponent.build_from_html);
502 //Roo.log("got echild:");
505 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
506 // and are not displayed -this causes this to use up the wrong element when matching.
507 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
510 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
511 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
517 //echild.dom.removeAttribute('xtype');
519 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
520 Roo.debug && Roo.log(self_cntr_el);
521 Roo.debug && Roo.log(echild);
522 Roo.debug && Roo.log(cn);
528 // if object has flexy:if - then it may or may not be rendered.
529 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
530 // skip a flexy if element.
531 Roo.debug && Roo.log('skipping render');
532 Roo.debug && Roo.log(tree);
534 Roo.debug && Roo.log('skipping all children');
535 skip_children = true;
540 // actually if flexy:foreach is found, we really want to create
541 // multiple copies here...
543 //Roo.log(this[cntr]());
544 // some elements do not have render methods.. like the layouts...
546 if(this[cntr](true) === false){
551 cn.render && cn.render(this[cntr](true));
554 // then add the element..
561 if (typeof (tree.menu) != 'undefined') {
562 tree.menu.parentType = cn.xtype;
563 tree.menu.triggerEl = cn.el;
564 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
568 if (!tree.items || !tree.items.length) {
570 //Roo.log(["no children", this]);
575 var items = tree.items;
578 //Roo.log(items.length);
580 if (!skip_children) {
581 for(var i =0;i < items.length;i++) {
582 // Roo.log(['add child', items[i]]);
583 nitems.push(cn.addxtype(items[i].xns == false ? items[i] : Roo.apply({}, items[i])));
589 //Roo.log("fire childrenrendered");
591 cn.fireEvent('childrenrendered', this);
597 * Set the element that will be used to show or hide
599 setVisibilityEl : function(el)
601 this.visibilityEl = el;
605 * Get the element that will be used to show or hide
607 getVisibilityEl : function()
609 if (typeof(this.visibilityEl) == 'object') {
610 return this.visibilityEl;
613 if (typeof(this.visibilityEl) == 'string') {
614 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
621 * Show a component - removes 'hidden' class
625 if(!this.getVisibilityEl()){
629 this.getVisibilityEl().removeClass(['hidden','d-none']);
631 this.fireEvent('show', this);
636 * Hide a component - adds 'hidden' class
640 if(!this.getVisibilityEl()){
644 this.getVisibilityEl().addClass(['hidden','d-none']);
646 this.fireEvent('hide', this);
659 * @class Roo.bootstrap.Element
660 * @extends Roo.bootstrap.Component
661 * @children Roo.bootstrap.Component
662 * Bootstrap Element class (basically a DIV used to make random stuff )
664 * @cfg {String} html contents of the element
665 * @cfg {String} tag tag of the element
666 * @cfg {String} cls class of the element
667 * @cfg {Boolean} preventDefault (true|false) default false
668 * @cfg {Boolean} clickable (true|false) default false
669 * @cfg {String} role default blank - set to button to force cursor pointer
673 * Create a new Element
674 * @param {Object} config The config object
677 Roo.bootstrap.Element = function(config){
678 Roo.bootstrap.Element.superclass.constructor.call(this, config);
684 * When a element is chick
685 * @param {Roo.bootstrap.Element} this
686 * @param {Roo.EventObject} e
694 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
699 preventDefault: false,
704 getAutoCreate : function(){
708 // cls: this.cls, double assign in parent class Component.js :: onRender
711 if (this.role !== false) {
712 cfg.role = this.role;
718 initEvents: function()
720 Roo.bootstrap.Element.superclass.initEvents.call(this);
723 this.el.on('click', this.onClick, this);
729 onClick : function(e)
731 if(this.preventDefault){
735 this.fireEvent('click', this, e); // why was this double click before?
743 getValue : function()
745 return this.el.dom.innerHTML;
748 setValue : function(value)
750 this.el.dom.innerHTML = value;
765 * @class Roo.bootstrap.DropTarget
766 * @extends Roo.bootstrap.Element
767 * Bootstrap DropTarget class
769 * @cfg {string} name dropable name
772 * Create a new Dropable Area
773 * @param {Object} config The config object
776 Roo.bootstrap.DropTarget = function(config){
777 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
783 * When a element is chick
784 * @param {Roo.bootstrap.Element} this
785 * @param {Roo.EventObject} e
791 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
794 getAutoCreate : function(){
799 initEvents: function()
801 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
802 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
805 drop : this.dragDrop.createDelegate(this),
806 enter : this.dragEnter.createDelegate(this),
807 out : this.dragOut.createDelegate(this),
808 over : this.dragOver.createDelegate(this)
812 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
815 dragDrop : function(source,e,data)
817 // user has to decide how to impliment this.
820 //this.fireEvent('drop', this, source, e ,data);
824 dragEnter : function(n, dd, e, data)
826 // probably want to resize the element to match the dropped element..
828 this.originalSize = this.el.getSize();
829 this.el.setSize( n.el.getSize());
830 this.dropZone.DDM.refreshCache(this.name);
831 Roo.log([n, dd, e, data]);
834 dragOut : function(value)
836 // resize back to normal
838 this.el.setSize(this.originalSize);
839 this.dropZone.resetConstraints();
842 dragOver : function()
859 * @class Roo.bootstrap.Body
860 * @extends Roo.bootstrap.Component
861 * @children Roo.bootstrap.Component
862 * @parent none builder
863 * Bootstrap Body class
867 * @param {Object} config The config object
870 Roo.bootstrap.Body = function(config){
872 config = config || {};
874 Roo.bootstrap.Body.superclass.constructor.call(this, config);
875 this.el = Roo.get(config.el ? config.el : document.body );
876 if (this.cls && this.cls.length) {
877 Roo.get(document.body).addClass(this.cls);
881 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
883 is_body : true,// just to make sure it's constructed?
888 onRender : function(ct, position)
890 /* Roo.log("Roo.bootstrap.Body - onRender");
891 if (this.cls && this.cls.length) {
892 Roo.get(document.body).addClass(this.cls);
911 * @class Roo.bootstrap.ButtonGroup
912 * @extends Roo.bootstrap.Component
913 * Bootstrap ButtonGroup class
914 * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
916 * @cfg {String} size lg | sm | xs (default empty normal)
917 * @cfg {String} align vertical | justified (default none)
918 * @cfg {String} direction up | down (default down)
919 * @cfg {Boolean} toolbar false | true
920 * @cfg {Boolean} btn true | false
925 * @param {Object} config The config object
928 Roo.bootstrap.ButtonGroup = function(config){
929 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
932 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
940 getAutoCreate : function(){
946 cfg.html = this.html || cfg.html;
957 if (['vertical','justified'].indexOf(this.align)!==-1) {
958 cfg.cls = 'btn-group-' + this.align;
960 if (this.align == 'justified') {
961 console.log(this.items);
965 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
966 cfg.cls += ' btn-group-' + this.size;
969 if (this.direction == 'up') {
970 cfg.cls += ' dropup' ;
976 * Add a button to the group (similar to NavItem API.)
978 addItem : function(cfg)
980 var cn = new Roo.bootstrap.Button(cfg);
982 cn.parentId = this.id;
983 cn.onRender(this.el, null);
997 * @class Roo.bootstrap.Button
998 * @extends Roo.bootstrap.Component
999 * Bootstrap Button class
1000 * @cfg {String} html The button content
1001 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
1002 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1003 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1004 * @cfg {String} size (lg|sm|xs)
1005 * @cfg {String} tag (a|input|submit)
1006 * @cfg {String} href empty or href
1007 * @cfg {Boolean} disabled default false;
1008 * @cfg {Boolean} isClose default false;
1009 * @cfg {String} glyphicon depricated - use fa
1010 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1011 * @cfg {String} badge text for badge
1012 * @cfg {String} theme (default|glow)
1013 * @cfg {Boolean} inverse dark themed version
1014 * @cfg {Boolean} toggle is it a slidy toggle button
1015 * @cfg {Boolean} pressed default null - if the button ahs active state
1016 * @cfg {String} ontext text for on slidy toggle state
1017 * @cfg {String} offtext text for off slidy toggle state
1018 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1019 * @cfg {Boolean} removeClass remove the standard class..
1020 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1021 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1022 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
1025 * Create a new button
1026 * @param {Object} config The config object
1030 Roo.bootstrap.Button = function(config){
1031 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1037 * When a button is pressed
1038 * @param {Roo.bootstrap.Button} btn
1039 * @param {Roo.EventObject} e
1044 * When a button is double clicked
1045 * @param {Roo.bootstrap.Button} btn
1046 * @param {Roo.EventObject} e
1051 * After the button has been toggles
1052 * @param {Roo.bootstrap.Button} btn
1053 * @param {Roo.EventObject} e
1054 * @param {boolean} pressed (also available as button.pressed)
1060 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1081 preventDefault: true,
1090 getAutoCreate : function(){
1098 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1099 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1100 this.tag = 'button';
1104 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1106 if (this.toggle == true) {
1109 cls: 'slider-frame roo-button',
1113 'data-on-text':'ON',
1114 'data-off-text':'OFF',
1115 cls: 'slider-button',
1120 // why are we validating the weights?
1121 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1122 cfg.cls += ' ' + this.weight;
1129 cfg.cls += ' close';
1131 cfg["aria-hidden"] = true;
1133 cfg.html = "×";
1139 if (this.theme==='default') {
1140 cfg.cls = 'btn roo-button';
1142 //if (this.parentType != 'Navbar') {
1143 this.weight = this.weight.length ? this.weight : 'default';
1145 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1147 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1148 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1149 cfg.cls += ' btn-' + outline + weight;
1150 if (this.weight == 'default') {
1152 cfg.cls += ' btn-' + this.weight;
1155 } else if (this.theme==='glow') {
1158 cfg.cls = 'btn-glow roo-button';
1160 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1162 cfg.cls += ' ' + this.weight;
1168 this.cls += ' inverse';
1172 if (this.active || this.pressed === true) {
1173 cfg.cls += ' active';
1176 if (this.disabled) {
1177 cfg.disabled = 'disabled';
1181 Roo.log('changing to ul' );
1183 this.glyphicon = 'caret';
1184 if (Roo.bootstrap.version == 4) {
1185 this.fa = 'caret-down';
1190 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1192 //gsRoo.log(this.parentType);
1193 if (this.parentType === 'Navbar' && !this.parent().bar) {
1194 Roo.log('changing to li?');
1203 href : this.href || '#'
1206 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1207 cfg.cls += ' dropdown';
1214 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1216 if (this.glyphicon) {
1217 cfg.html = ' ' + cfg.html;
1222 cls: 'glyphicon glyphicon-' + this.glyphicon
1227 cfg.html = ' ' + cfg.html;
1232 cls: 'fa fas fa-' + this.fa
1242 // cfg.cls='btn roo-button';
1246 var value = cfg.html;
1251 cls: 'glyphicon glyphicon-' + this.glyphicon,
1258 cls: 'fa fas fa-' + this.fa,
1263 var bw = this.badge_weight.length ? this.badge_weight :
1264 (this.weight.length ? this.weight : 'secondary');
1265 bw = bw == 'default' ? 'secondary' : bw;
1271 cls: 'badge badge-' + bw,
1280 cfg.cls += ' dropdown';
1281 cfg.html = typeof(cfg.html) != 'undefined' ?
1282 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1285 if (cfg.tag !== 'a' && this.href !== '') {
1286 throw "Tag must be a to set href.";
1287 } else if (this.href.length > 0) {
1288 cfg.href = this.href;
1291 if(this.removeClass){
1296 cfg.target = this.target;
1301 initEvents: function() {
1302 // Roo.log('init events?');
1303 // Roo.log(this.el.dom);
1306 if (typeof (this.menu) != 'undefined') {
1307 this.menu.parentType = this.xtype;
1308 this.menu.triggerEl = this.el;
1309 this.addxtype(Roo.apply({}, this.menu));
1313 if (this.el.hasClass('roo-button')) {
1314 this.el.on('click', this.onClick, this);
1315 this.el.on('dblclick', this.onDblClick, this);
1317 this.el.select('.roo-button').on('click', this.onClick, this);
1318 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1322 if(this.removeClass){
1323 this.el.on('click', this.onClick, this);
1326 if (this.group === true) {
1327 if (this.pressed === false || this.pressed === true) {
1330 this.pressed = false;
1331 this.setActive(this.pressed);
1336 this.el.enableDisplayMode();
1339 onClick : function(e)
1341 if (this.disabled) {
1345 Roo.log('button on click ');
1346 if(this.href === '' || this.preventDefault){
1355 this.setActive(true);
1356 var pi = this.parent().items;
1357 for (var i = 0;i < pi.length;i++) {
1358 if (this == pi[i]) {
1361 if (pi[i].el.hasClass('roo-button')) {
1362 pi[i].setActive(false);
1365 this.fireEvent('click', this, e);
1369 if (this.pressed === true || this.pressed === false) {
1370 this.toggleActive(e);
1374 this.fireEvent('click', this, e);
1376 onDblClick: function(e)
1378 if (this.disabled) {
1381 if(this.preventDefault){
1384 this.fireEvent('dblclick', this, e);
1387 * Enables this button
1391 this.disabled = false;
1392 this.el.removeClass('disabled');
1393 this.el.dom.removeAttribute("disabled");
1397 * Disable this button
1399 disable : function()
1401 this.disabled = true;
1402 this.el.addClass('disabled');
1403 this.el.attr("disabled", "disabled")
1406 * sets the active state on/off,
1407 * @param {Boolean} state (optional) Force a particular state
1409 setActive : function(v) {
1411 this.el[v ? 'addClass' : 'removeClass']('active');
1415 * toggles the current active state
1417 toggleActive : function(e)
1419 this.setActive(!this.pressed); // this modifies pressed...
1420 this.fireEvent('toggle', this, e, this.pressed);
1423 * get the current active state
1424 * @return {boolean} true if it's active
1426 isActive : function()
1428 return this.el.hasClass('active');
1431 * set the text of the first selected button
1433 setText : function(str)
1435 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1438 * get the text of the first selected button
1440 getText : function()
1442 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1445 setWeight : function(str)
1447 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1448 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1450 var outline = this.outline ? 'outline-' : '';
1451 if (str == 'default') {
1452 this.el.addClass('btn-default btn-outline-secondary');
1455 this.el.addClass('btn-' + outline + str);
1460 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1462 Roo.bootstrap.Button.weights = [
1482 * @class Roo.bootstrap.Column
1483 * @extends Roo.bootstrap.Component
1484 * @children Roo.bootstrap.Component
1485 * Bootstrap Column class
1486 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1487 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1488 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1489 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1490 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1491 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1492 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1493 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1496 * @cfg {Boolean} hidden (true|false) hide the element
1497 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1498 * @cfg {String} fa (ban|check|...) font awesome icon
1499 * @cfg {Number} fasize (1|2|....) font awsome size
1501 * @cfg {String} icon (info-sign|check|...) glyphicon name
1503 * @cfg {String} html content of column.
1506 * Create a new Column
1507 * @param {Object} config The config object
1510 Roo.bootstrap.Column = function(config){
1511 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1514 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1532 getAutoCreate : function(){
1533 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1541 var sizes = ['xs','sm','md','lg'];
1542 sizes.map(function(size ,ix){
1543 //Roo.log( size + ':' + settings[size]);
1545 if (settings[size+'off'] !== false) {
1546 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1549 if (settings[size] === false) {
1553 if (!settings[size]) { // 0 = hidden
1554 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1556 for (var i = ix; i > -1; i--) {
1557 cfg.cls += ' d-' + sizes[i] + '-none';
1563 cfg.cls += ' col-' + size + '-' + settings[size] + (
1564 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1570 cfg.cls += ' hidden';
1573 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1574 cfg.cls +=' alert alert-' + this.alert;
1578 if (this.html.length) {
1579 cfg.html = this.html;
1583 if (this.fasize > 1) {
1584 fasize = ' fa-' + this.fasize + 'x';
1586 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1591 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1610 * @class Roo.bootstrap.Container
1611 * @extends Roo.bootstrap.Component
1612 * @children Roo.bootstrap.Component
1614 * Bootstrap Container class
1615 * @cfg {Boolean} jumbotron is it a jumbotron element
1616 * @cfg {String} html content of element
1617 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1618 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1619 * @cfg {String} header content of header (for panel)
1620 * @cfg {String} footer content of footer (for panel)
1621 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1622 * @cfg {String} tag (header|aside|section) type of HTML tag.
1623 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1624 * @cfg {String} fa font awesome icon
1625 * @cfg {String} icon (info-sign|check|...) glyphicon name
1626 * @cfg {Boolean} hidden (true|false) hide the element
1627 * @cfg {Boolean} expandable (true|false) default false
1628 * @cfg {Boolean} expanded (true|false) default true
1629 * @cfg {String} rheader contet on the right of header
1630 * @cfg {Boolean} clickable (true|false) default false
1634 * Create a new Container
1635 * @param {Object} config The config object
1638 Roo.bootstrap.Container = function(config){
1639 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1645 * After the panel has been expand
1647 * @param {Roo.bootstrap.Container} this
1652 * After the panel has been collapsed
1654 * @param {Roo.bootstrap.Container} this
1659 * When a element is chick
1660 * @param {Roo.bootstrap.Container} this
1661 * @param {Roo.EventObject} e
1667 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1685 getChildContainer : function() {
1691 if (this.panel.length) {
1692 return this.el.select('.panel-body',true).first();
1699 getAutoCreate : function(){
1702 tag : this.tag || 'div',
1706 if (this.jumbotron) {
1707 cfg.cls = 'jumbotron';
1712 // - this is applied by the parent..
1714 // cfg.cls = this.cls + '';
1717 if (this.sticky.length) {
1719 var bd = Roo.get(document.body);
1720 if (!bd.hasClass('bootstrap-sticky')) {
1721 bd.addClass('bootstrap-sticky');
1722 Roo.select('html',true).setStyle('height', '100%');
1725 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1729 if (this.well.length) {
1730 switch (this.well) {
1733 cfg.cls +=' well well-' +this.well;
1742 cfg.cls += ' hidden';
1746 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1747 cfg.cls +=' alert alert-' + this.alert;
1752 if (this.panel.length) {
1753 cfg.cls += ' panel panel-' + this.panel;
1755 if (this.header.length) {
1759 if(this.expandable){
1761 cfg.cls = cfg.cls + ' expandable';
1765 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1773 cls : 'panel-title',
1774 html : (this.expandable ? ' ' : '') + this.header
1778 cls: 'panel-header-right',
1784 cls : 'panel-heading',
1785 style : this.expandable ? 'cursor: pointer' : '',
1793 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1798 if (this.footer.length) {
1800 cls : 'panel-footer',
1809 body.html = this.html || cfg.html;
1810 // prefix with the icons..
1812 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1815 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1820 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1821 cfg.cls = 'container';
1827 initEvents: function()
1829 if(this.expandable){
1830 var headerEl = this.headerEl();
1833 headerEl.on('click', this.onToggleClick, this);
1838 this.el.on('click', this.onClick, this);
1843 onToggleClick : function()
1845 var headerEl = this.headerEl();
1861 if(this.fireEvent('expand', this)) {
1863 this.expanded = true;
1865 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1867 this.el.select('.panel-body',true).first().removeClass('hide');
1869 var toggleEl = this.toggleEl();
1875 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1880 collapse : function()
1882 if(this.fireEvent('collapse', this)) {
1884 this.expanded = false;
1886 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1887 this.el.select('.panel-body',true).first().addClass('hide');
1889 var toggleEl = this.toggleEl();
1895 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1899 toggleEl : function()
1901 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1905 return this.el.select('.panel-heading .fa',true).first();
1908 headerEl : function()
1910 if(!this.el || !this.panel.length || !this.header.length){
1914 return this.el.select('.panel-heading',true).first()
1919 if(!this.el || !this.panel.length){
1923 return this.el.select('.panel-body',true).first()
1926 titleEl : function()
1928 if(!this.el || !this.panel.length || !this.header.length){
1932 return this.el.select('.panel-title',true).first();
1935 setTitle : function(v)
1937 var titleEl = this.titleEl();
1943 titleEl.dom.innerHTML = v;
1946 getTitle : function()
1949 var titleEl = this.titleEl();
1955 return titleEl.dom.innerHTML;
1958 setRightTitle : function(v)
1960 var t = this.el.select('.panel-header-right',true).first();
1966 t.dom.innerHTML = v;
1969 onClick : function(e)
1973 this.fireEvent('click', this, e);
1978 * @class Roo.bootstrap.Card
1979 * @extends Roo.bootstrap.Component
1980 * @children Roo.bootstrap.Component
1982 * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1985 * possible... may not be implemented..
1986 * @cfg {String} header_image src url of image.
1987 * @cfg {String|Object} header
1988 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1989 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1991 * @cfg {String} title
1992 * @cfg {String} subtitle
1993 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1994 * @cfg {String} footer
1996 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1998 * @cfg {String} margin (0|1|2|3|4|5|auto)
1999 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
2000 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
2001 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
2002 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2003 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2004 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2006 * @cfg {String} padding (0|1|2|3|4|5)
2007 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2008 * @cfg {String} padding_bottom (0|1|2|3|4|5)
2009 * @cfg {String} padding_left (0|1|2|3|4|5)
2010 * @cfg {String} padding_right (0|1|2|3|4|5)
2011 * @cfg {String} padding_x (0|1|2|3|4|5)
2012 * @cfg {String} padding_y (0|1|2|3|4|5)
2014 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2016 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2017 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2018 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2020 * @config {Boolean} dragable if this card can be dragged.
2021 * @config {String} drag_group group for drag
2022 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2023 * @config {String} drop_group group for drag
2025 * @config {Boolean} collapsable can the body be collapsed.
2026 * @config {Boolean} collapsed is the body collapsed when rendered...
2027 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2028 * @config {Boolean} rotated is the body rotated when rendered...
2031 * Create a new Container
2032 * @param {Object} config The config object
2035 Roo.bootstrap.Card = function(config){
2036 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2042 * When a element a card is dropped
2043 * @param {Roo.bootstrap.Card} this
2046 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2047 * @param {String} position 'above' or 'below'
2048 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2054 * When a element a card is rotate
2055 * @param {Roo.bootstrap.Card} this
2056 * @param {Roo.Element} n the node being dropped?
2057 * @param {Boolean} rotate status
2062 * When a card element is dragged over ready to drop (return false to block dropable)
2063 * @param {Roo.bootstrap.Card} this
2064 * @param {Object} data from dragdrop
2072 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2077 margin: '', /// may be better in component?
2107 collapsable : false,
2116 childContainer : false,
2117 dropEl : false, /// the dom placeholde element that indicates drop location.
2118 containerEl: false, // body container
2119 bodyEl: false, // card-body
2120 headerContainerEl : false, //
2122 header_imageEl : false,
2125 layoutCls : function()
2129 Roo.log(this.margin_bottom.length);
2130 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2131 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2133 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2134 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2136 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2137 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2141 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2142 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2143 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2147 // more generic support?
2155 // Roo.log("Call onRender: " + this.xtype);
2156 /* We are looking at something like this.
2158 <img src="..." class="card-img-top" alt="...">
2159 <div class="card-body">
2160 <h5 class="card-title">Card title</h5>
2161 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2163 >> this bit is really the body...
2164 <div> << we will ad dthis in hopefully it will not break shit.
2166 ** card text does not actually have any styling...
2168 <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>
2171 <a href="#" class="card-link">Card link</a>
2174 <div class="card-footer">
2175 <small class="text-muted">Last updated 3 mins ago</small>
2179 getAutoCreate : function(){
2187 if (this.weight.length && this.weight != 'light') {
2188 cfg.cls += ' text-white';
2190 cfg.cls += ' text-dark'; // need as it's nested..
2192 if (this.weight.length) {
2193 cfg.cls += ' bg-' + this.weight;
2196 cfg.cls += ' ' + this.layoutCls();
2199 var hdr_ctr = false;
2200 if (this.header.length) {
2202 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2203 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2211 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2217 if (this.collapsable) {
2220 cls : 'd-block user-select-none',
2224 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2229 hdr.cn.push(hdr_ctr);
2234 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2239 if (this.header_image.length) {
2242 cls : 'card-img-top',
2243 src: this.header_image // escape?
2248 cls : 'card-img-top d-none'
2254 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2258 if (this.collapsable || this.rotateable) {
2261 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2268 if (this.title.length) {
2272 src: this.title // escape?
2276 if (this.subtitle.length) {
2280 src: this.subtitle // escape?
2286 cls : 'roo-card-body-ctr'
2289 if (this.html.length) {
2295 // fixme ? handle objects?
2297 if (this.footer.length) {
2300 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2305 cfg.cn.push({cls : 'card-footer d-none'});
2314 getCardHeader : function()
2316 var ret = this.el.select('.card-header',true).first();
2317 if (ret.hasClass('d-none')) {
2318 ret.removeClass('d-none');
2323 getCardFooter : function()
2325 var ret = this.el.select('.card-footer',true).first();
2326 if (ret.hasClass('d-none')) {
2327 ret.removeClass('d-none');
2332 getCardImageTop : function()
2334 var ret = this.header_imageEl;
2335 if (ret.hasClass('d-none')) {
2336 ret.removeClass('d-none');
2342 getChildContainer : function()
2348 return this.el.select('.roo-card-body-ctr',true).first();
2351 initEvents: function()
2353 this.bodyEl = this.el.select('.card-body',true).first();
2354 this.containerEl = this.getChildContainer();
2356 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2357 containerScroll: true,
2358 ddGroup: this.drag_group || 'default_card_drag_group'
2360 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2362 if (this.dropable) {
2363 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2364 containerScroll: true,
2365 ddGroup: this.drop_group || 'default_card_drag_group'
2367 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2368 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2369 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2370 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2371 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2374 if (this.collapsable) {
2375 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2377 if (this.rotateable) {
2378 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2380 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2382 this.footerEl = this.el.select('.card-footer',true).first();
2383 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2384 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2385 this.headerEl = this.el.select('.card-header',true).first();
2388 this.el.addClass('roo-card-rotated');
2389 this.fireEvent('rotate', this, true);
2391 this.header_imageEl = this.el.select('.card-img-top',true).first();
2392 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2395 getDragData : function(e)
2397 var target = this.getEl();
2399 //this.handleSelection(e);
2404 nodes: this.getEl(),
2409 dragData.ddel = target.dom ; // the div element
2410 Roo.log(target.getWidth( ));
2411 dragData.ddel.style.width = target.getWidth() + 'px';
2418 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2419 * whole Element becomes the target, and this causes the drop gesture to append.
2421 * Returns an object:
2424 position : 'below' or 'above'
2425 card : relateive to card OBJECT (or true for no cards listed)
2426 items_n : relative to nth item in list
2427 card_n : relative to nth card in list
2432 getTargetFromEvent : function(e, dragged_card_el)
2434 var target = e.getTarget();
2435 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2436 target = target.parentNode;
2447 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2448 // see if target is one of the 'cards'...
2451 //Roo.log(this.items.length);
2454 var last_card_n = 0;
2456 for (var i = 0;i< this.items.length;i++) {
2458 if (!this.items[i].el.hasClass('card')) {
2461 pos = this.getDropPoint(e, this.items[i].el.dom);
2463 cards_len = ret.cards.length;
2464 //Roo.log(this.items[i].el.dom.id);
2465 ret.cards.push(this.items[i]);
2467 if (ret.card_n < 0 && pos == 'above') {
2468 ret.position = cards_len > 0 ? 'below' : pos;
2469 ret.items_n = i > 0 ? i - 1 : 0;
2470 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2471 ret.card = ret.cards[ret.card_n];
2474 if (!ret.cards.length) {
2476 ret.position = 'below';
2480 // could not find a card.. stick it at the end..
2481 if (ret.card_n < 0) {
2482 ret.card_n = last_card_n;
2483 ret.card = ret.cards[last_card_n];
2484 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2485 ret.position = 'below';
2488 if (this.items[ret.items_n].el == dragged_card_el) {
2492 if (ret.position == 'below') {
2493 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2495 if (card_after && card_after.el == dragged_card_el) {
2502 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2504 if (card_before && card_before.el == dragged_card_el) {
2511 onNodeEnter : function(n, dd, e, data){
2514 onNodeOver : function(n, dd, e, data)
2517 var target_info = this.getTargetFromEvent(e,data.source.el);
2518 if (target_info === false) {
2519 this.dropPlaceHolder('hide');
2522 Roo.log(['getTargetFromEvent', target_info ]);
2525 if (this.fireEvent('cardover', this, [ data ]) === false) {
2529 this.dropPlaceHolder('show', target_info,data);
2533 onNodeOut : function(n, dd, e, data){
2534 this.dropPlaceHolder('hide');
2537 onNodeDrop : function(n, dd, e, data)
2540 // call drop - return false if
2542 // this could actually fail - if the Network drops..
2543 // we will ignore this at present..- client should probably reload
2544 // the whole set of cards if stuff like that fails.
2547 var info = this.getTargetFromEvent(e,data.source.el);
2548 if (info === false) {
2551 this.dropPlaceHolder('hide');
2555 this.acceptCard(data.source, info.position, info.card, info.items_n);
2559 firstChildCard : function()
2561 for (var i = 0;i< this.items.length;i++) {
2563 if (!this.items[i].el.hasClass('card')) {
2566 return this.items[i];
2568 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2573 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2575 acceptCard : function(move_card, position, next_to_card )
2577 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2581 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2583 move_card.parent().removeCard(move_card);
2586 var dom = move_card.el.dom;
2587 dom.style.width = ''; // clear with - which is set by drag.
2589 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2590 var cardel = next_to_card.el.dom;
2592 if (position == 'above' ) {
2593 cardel.parentNode.insertBefore(dom, cardel);
2594 } else if (cardel.nextSibling) {
2595 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2597 cardel.parentNode.append(dom);
2600 // card container???
2601 this.containerEl.dom.append(dom);
2604 //FIXME HANDLE card = true
2606 // add this to the correct place in items.
2608 // remove Card from items.
2611 if (this.items.length) {
2613 //Roo.log([info.items_n, info.position, this.items.length]);
2614 for (var i =0; i < this.items.length; i++) {
2615 if (i == to_items_n && position == 'above') {
2616 nitems.push(move_card);
2618 nitems.push(this.items[i]);
2619 if (i == to_items_n && position == 'below') {
2620 nitems.push(move_card);
2623 this.items = nitems;
2624 Roo.log(this.items);
2626 this.items.push(move_card);
2629 move_card.parentId = this.id;
2635 removeCard : function(c)
2637 this.items = this.items.filter(function(e) { return e != c });
2640 dom.parentNode.removeChild(dom);
2641 dom.style.width = ''; // clear with - which is set by drag.
2646 /** Decide whether to drop above or below a View node. */
2647 getDropPoint : function(e, n, dd)
2652 if (n == this.containerEl.dom) {
2655 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2656 var c = t + (b - t) / 2;
2657 var y = Roo.lib.Event.getPageY(e);
2664 onToggleCollapse : function(e)
2666 if (this.collapsed) {
2667 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2668 this.collapsableEl.addClass('show');
2669 this.collapsed = false;
2672 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2673 this.collapsableEl.removeClass('show');
2674 this.collapsed = true;
2679 onToggleRotate : function(e)
2681 this.collapsableEl.removeClass('show');
2682 this.footerEl.removeClass('d-none');
2683 this.el.removeClass('roo-card-rotated');
2684 this.el.removeClass('d-none');
2687 this.collapsableEl.addClass('show');
2688 this.rotated = false;
2689 this.fireEvent('rotate', this, this.rotated);
2692 this.el.addClass('roo-card-rotated');
2693 this.footerEl.addClass('d-none');
2694 this.el.select('.roo-collapsable').removeClass('show');
2696 this.rotated = true;
2697 this.fireEvent('rotate', this, this.rotated);
2701 dropPlaceHolder: function (action, info, data)
2703 if (this.dropEl === false) {
2704 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2708 this.dropEl.removeClass(['d-none', 'd-block']);
2709 if (action == 'hide') {
2711 this.dropEl.addClass('d-none');
2714 // FIXME - info.card == true!!!
2715 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2717 if (info.card !== true) {
2718 var cardel = info.card.el.dom;
2720 if (info.position == 'above') {
2721 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2722 } else if (cardel.nextSibling) {
2723 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2725 cardel.parentNode.append(this.dropEl.dom);
2728 // card container???
2729 this.containerEl.dom.append(this.dropEl.dom);
2732 this.dropEl.addClass('d-block roo-card-dropzone');
2734 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2741 setHeaderText: function(html)
2744 if (this.headerContainerEl) {
2745 this.headerContainerEl.dom.innerHTML = html;
2748 onHeaderImageLoad : function(ev, he)
2750 if (!this.header_image_fit_square) {
2754 var hw = he.naturalHeight / he.naturalWidth;
2757 //var w = he.dom.naturalWidth;
2760 he.style.position = 'relative';
2762 var nw = (ww * (1/hw));
2763 Roo.get(he).setSize( ww * (1/hw), ww);
2764 he.style.left = ((ww - nw)/ 2) + 'px';
2765 he.style.position = 'relative';
2776 * Card header - holder for the card header elements.
2781 * @class Roo.bootstrap.CardHeader
2782 * @extends Roo.bootstrap.Element
2783 * @parent Roo.bootstrap.Card
2784 * @children Roo.bootstrap.Component
2785 * Bootstrap CardHeader class
2787 * Create a new Card Header - that you can embed children into
2788 * @param {Object} config The config object
2791 Roo.bootstrap.CardHeader = function(config){
2792 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2795 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2798 container_method : 'getCardHeader'
2811 * Card footer - holder for the card footer elements.
2816 * @class Roo.bootstrap.CardFooter
2817 * @extends Roo.bootstrap.Element
2818 * @parent Roo.bootstrap.Card
2819 * @children Roo.bootstrap.Component
2820 * Bootstrap CardFooter class
2823 * Create a new Card Footer - that you can embed children into
2824 * @param {Object} config The config object
2827 Roo.bootstrap.CardFooter = function(config){
2828 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2831 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2834 container_method : 'getCardFooter'
2847 * Card header - holder for the card header elements.
2852 * @class Roo.bootstrap.CardImageTop
2853 * @extends Roo.bootstrap.Element
2854 * @parent Roo.bootstrap.Card
2855 * @children Roo.bootstrap.Component
2856 * Bootstrap CardImageTop class
2859 * Create a new Card Image Top container
2860 * @param {Object} config The config object
2863 Roo.bootstrap.CardImageTop = function(config){
2864 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2867 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2870 container_method : 'getCardImageTop'
2885 * @class Roo.bootstrap.ButtonUploader
2886 * @extends Roo.bootstrap.Button
2887 * Bootstrap Button Uploader class - it's a button which when you add files to it
2890 * @cfg {Number} errorTimeout default 3000
2891 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2892 * @cfg {Array} html The button text.
2893 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2896 * Create a new CardUploader
2897 * @param {Object} config The config object
2900 Roo.bootstrap.ButtonUploader = function(config){
2904 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2910 * @event beforeselect
2911 * When button is pressed, before show upload files dialog is shown
2912 * @param {Roo.bootstrap.UploaderButton} this
2915 'beforeselect' : true,
2917 * @event fired when files have been selected,
2918 * When a the download link is clicked
2919 * @param {Roo.bootstrap.UploaderButton} this
2920 * @param {Array} Array of files that have been uploaded
2927 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2930 errorTimeout : 3000,
2934 fileCollection : false,
2939 getAutoCreate : function()
2946 Roo.bootstrap.Button.prototype.getAutoCreate.call(this)
2954 initEvents : function()
2957 Roo.bootstrap.Button.prototype.initEvents.call(this);
2963 this.urlAPI = (window.createObjectURL && window) ||
2964 (window.URL && URL.revokeObjectURL && URL) ||
2965 (window.webkitURL && webkitURL);
2970 cls : 'd-none roo-card-upload-selector'
2973 if (this.multiple) {
2974 im.multiple = 'multiple';
2976 this.selectorEl = Roo.get(document.body).createChild(im); // so it does not capture click event for navitem.
2978 //this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2980 this.selectorEl.on('change', this.onFileSelected, this);
2987 onClick : function(e)
2991 if ( this.fireEvent('beforeselect', this) === false) {
2995 this.selectorEl.dom.click();
2999 onFileSelected : function(e)
3003 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3006 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3007 this.selectorEl.dom.value = '';// hopefully reset..
3009 this.fireEvent('uploaded', this, files );
3017 * addCard - add an Attachment to the uploader
3018 * @param data - the data about the image to upload
3022 title : "Title of file",
3023 is_uploaded : false,
3024 src : "http://.....",
3025 srcfile : { the File upload object },
3026 mimetype : file.type,
3029 .. any other data...
3054 * @class Roo.bootstrap.Img
3055 * @extends Roo.bootstrap.Component
3056 * Bootstrap Img class
3057 * @cfg {Boolean} imgResponsive false | true
3058 * @cfg {String} border rounded | circle | thumbnail
3059 * @cfg {String} src image source
3060 * @cfg {String} alt image alternative text
3061 * @cfg {String} href a tag href
3062 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3063 * @cfg {String} xsUrl xs image source
3064 * @cfg {String} smUrl sm image source
3065 * @cfg {String} mdUrl md image source
3066 * @cfg {String} lgUrl lg image source
3067 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3070 * Create a new Input
3071 * @param {Object} config The config object
3074 Roo.bootstrap.Img = function(config){
3075 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3081 * The img click event for the img.
3082 * @param {Roo.EventObject} e
3087 * The when any image loads
3088 * @param {Roo.EventObject} e
3094 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3096 imgResponsive: true,
3098 src: 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=',
3105 backgroundContain : false,
3107 getAutoCreate : function()
3109 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3110 return this.createSingleImg();
3115 cls: 'roo-image-responsive-group',
3120 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3122 if(!_this[size + 'Url']){
3128 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3129 html: _this.html || cfg.html,
3130 src: _this[size + 'Url']
3133 img.cls += ' roo-image-responsive-' + size;
3135 var s = ['xs', 'sm', 'md', 'lg'];
3137 s.splice(s.indexOf(size), 1);
3139 Roo.each(s, function(ss){
3140 img.cls += ' hidden-' + ss;
3143 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3144 cfg.cls += ' img-' + _this.border;
3148 cfg.alt = _this.alt;
3161 a.target = _this.target;
3165 cfg.cn.push((_this.href) ? a : img);
3172 createSingleImg : function()
3176 cls: (this.imgResponsive) ? 'img-responsive' : '',
3178 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3181 if (this.backgroundContain) {
3182 cfg.cls += ' background-contain';
3185 cfg.html = this.html || cfg.html;
3187 if (this.backgroundContain) {
3188 cfg.style="background-image: url(" + this.src + ')';
3190 cfg.src = this.src || cfg.src;
3193 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3194 cfg.cls += ' img-' + this.border;
3211 a.target = this.target;
3216 return (this.href) ? a : cfg;
3219 initEvents: function()
3222 this.el.on('click', this.onClick, this);
3224 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3225 this.el.on('load', this.onImageLoad, this);
3227 // not sure if this works.. not tested
3228 this.el.select('img', true).on('load', this.onImageLoad, this);
3233 onClick : function(e)
3235 Roo.log('img onclick');
3236 this.fireEvent('click', this, e);
3238 onImageLoad: function(e)
3240 Roo.log('img load');
3241 this.fireEvent('load', this, e);
3245 * Sets the url of the image - used to update it
3246 * @param {String} url the url of the image
3249 setSrc : function(url)
3253 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3254 if (this.backgroundContain) {
3255 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3257 this.el.dom.src = url;
3262 this.el.select('img', true).first().dom.src = url;
3278 * @class Roo.bootstrap.Link
3279 * @extends Roo.bootstrap.Component
3280 * @children Roo.bootstrap.Component
3281 * Bootstrap Link Class (eg. '<a href>')
3283 * @cfg {String} alt image alternative text
3284 * @cfg {String} href a tag href
3285 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3286 * @cfg {String} html the content of the link.
3287 * @cfg {String} anchor name for the anchor link
3288 * @cfg {String} fa - favicon
3290 * @cfg {Boolean} preventDefault (true | false) default false
3294 * Create a new Input
3295 * @param {Object} config The config object
3298 Roo.bootstrap.Link = function(config){
3299 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3305 * The img click event for the img.
3306 * @param {Roo.EventObject} e
3312 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3316 preventDefault: false,
3322 getAutoCreate : function()
3324 var html = this.html || '';
3326 if (this.fa !== false) {
3327 html = '<i class="fa fa-' + this.fa + '"></i>';
3332 // anchor's do not require html/href...
3333 if (this.anchor === false) {
3335 cfg.href = this.href || '#';
3337 cfg.name = this.anchor;
3338 if (this.html !== false || this.fa !== false) {
3341 if (this.href !== false) {
3342 cfg.href = this.href;
3346 if(this.alt !== false){
3351 if(this.target !== false) {
3352 cfg.target = this.target;
3358 initEvents: function() {
3360 if(!this.href || this.preventDefault){
3361 this.el.on('click', this.onClick, this);
3365 onClick : function(e)
3367 if(this.preventDefault){
3370 //Roo.log('img onclick');
3371 this.fireEvent('click', this, e);
3384 * @class Roo.bootstrap.Header
3385 * @extends Roo.bootstrap.Component
3386 * @children Roo.bootstrap.Component
3387 * Bootstrap Header class
3390 * @cfg {String} html content of header
3391 * @cfg {Number} level (1|2|3|4|5|6) default 1
3394 * Create a new Header
3395 * @param {Object} config The config object
3399 Roo.bootstrap.Header = function(config){
3400 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3403 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3411 getAutoCreate : function(){
3416 tag: 'h' + (1 *this.level),
3417 html: this.html || ''
3428 * @class Roo.bootstrap.MenuMgr
3430 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3433 Roo.bootstrap.menu.Manager = function(){
3434 var menus, active, groups = {}, attached = false, lastShow = new Date();
3436 // private - called when first menu is created
3439 active = new Roo.util.MixedCollection();
3440 Roo.get(document).addKeyListener(27, function(){
3441 if(active.length > 0){
3449 if(active && active.length > 0){
3450 var c = active.clone();
3460 if(active.length < 1){
3461 Roo.get(document).un("mouseup", onMouseDown);
3469 var last = active.last();
3470 lastShow = new Date();
3473 Roo.get(document).on("mouseup", onMouseDown);
3478 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3479 m.parentMenu.activeChild = m;
3480 }else if(last && last.isVisible()){
3481 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3486 function onBeforeHide(m){
3488 m.activeChild.hide();
3490 if(m.autoHideTimer){
3491 clearTimeout(m.autoHideTimer);
3492 delete m.autoHideTimer;
3497 function onBeforeShow(m){
3498 var pm = m.parentMenu;
3499 if(!pm && !m.allowOtherMenus){
3501 }else if(pm && pm.activeChild && active != m){
3502 pm.activeChild.hide();
3506 // private this should really trigger on mouseup..
3507 function onMouseDown(e){
3508 Roo.log("on Mouse Up");
3510 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3511 Roo.log("MenuManager hideAll");
3520 function onBeforeCheck(mi, state){
3522 var g = groups[mi.group];
3523 for(var i = 0, l = g.length; i < l; i++){
3525 g[i].setChecked(false);
3534 * Hides all menus that are currently visible
3536 hideAll : function(){
3541 register : function(menu){
3545 menus[menu.id] = menu;
3546 menu.on("beforehide", onBeforeHide);
3547 menu.on("hide", onHide);
3548 menu.on("beforeshow", onBeforeShow);
3549 menu.on("show", onShow);
3551 if(g && menu.events["checkchange"]){
3555 groups[g].push(menu);
3556 menu.on("checkchange", onCheck);
3561 * Returns a {@link Roo.menu.Menu} object
3562 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3563 * be used to generate and return a new Menu instance.
3565 get : function(menu){
3566 if(typeof menu == "string"){ // menu id
3568 }else if(menu.events){ // menu instance
3571 /*else if(typeof menu.length == 'number'){ // array of menu items?
3572 return new Roo.bootstrap.Menu({items:menu});
3573 }else{ // otherwise, must be a config
3574 return new Roo.bootstrap.Menu(menu);
3581 unregister : function(menu){
3582 delete menus[menu.id];
3583 menu.un("beforehide", onBeforeHide);
3584 menu.un("hide", onHide);
3585 menu.un("beforeshow", onBeforeShow);
3586 menu.un("show", onShow);
3588 if(g && menu.events["checkchange"]){
3589 groups[g].remove(menu);
3590 menu.un("checkchange", onCheck);
3595 registerCheckable : function(menuItem){
3596 var g = menuItem.group;
3601 groups[g].push(menuItem);
3602 menuItem.on("beforecheckchange", onBeforeCheck);
3607 unregisterCheckable : function(menuItem){
3608 var g = menuItem.group;
3610 groups[g].remove(menuItem);
3611 menuItem.un("beforecheckchange", onBeforeCheck);
3617 * @class Roo.bootstrap.menu.Menu
3618 * @extends Roo.bootstrap.Component
3620 * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3622 * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3624 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3625 * @cfg {bool} hidden if the menu should be hidden when rendered.
3626 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3627 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3628 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3629 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3633 * @param {Object} config The config objectQ
3637 Roo.bootstrap.menu.Menu = function(config){
3639 if (config.type == 'treeview') {
3640 // normally menu's are drawn attached to the document to handle layering etc..
3641 // however treeview (used by the docs menu is drawn into the parent element)
3642 this.container_method = 'getChildContainer';
3645 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3646 if (this.registerMenu && this.type != 'treeview') {
3647 Roo.bootstrap.menu.Manager.register(this);
3654 * Fires before this menu is displayed (return false to block)
3655 * @param {Roo.menu.Menu} this
3660 * Fires before this menu is hidden (return false to block)
3661 * @param {Roo.menu.Menu} this
3666 * Fires after this menu is displayed
3667 * @param {Roo.menu.Menu} this
3672 * Fires after this menu is hidden
3673 * @param {Roo.menu.Menu} this
3678 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3679 * @param {Roo.menu.Menu} this
3680 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3681 * @param {Roo.EventObject} e
3686 * Fires when the mouse is hovering over this menu
3687 * @param {Roo.menu.Menu} this
3688 * @param {Roo.EventObject} e
3689 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3694 * Fires when the mouse exits 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 a menu item contained in this menu is clicked
3703 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3704 * @param {Roo.EventObject} e
3708 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3711 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
3715 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3718 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3720 registerMenu : true,
3722 menuItems :false, // stores the menu items..
3732 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3734 hideTrigger : false,
3739 getChildContainer : function() {
3743 getAutoCreate : function(){
3745 //if (['right'].indexOf(this.align)!==-1) {
3746 // cfg.cn[1].cls += ' pull-right'
3751 cls : 'dropdown-menu shadow' ,
3752 style : 'z-index:1000'
3756 if (this.type === 'submenu') {
3757 cfg.cls = 'submenu active';
3759 if (this.type === 'treeview') {
3760 cfg.cls = 'treeview-menu';
3765 initEvents : function() {
3767 // Roo.log("ADD event");
3768 // Roo.log(this.triggerEl.dom);
3769 if (this.triggerEl) {
3771 this.triggerEl.on('click', this.onTriggerClick, this);
3773 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3775 if (!this.hideTrigger) {
3776 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3777 // dropdown toggle on the 'a' in BS4?
3778 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3780 this.triggerEl.addClass('dropdown-toggle');
3786 this.el.on('touchstart' , this.onTouch, this);
3788 this.el.on('click' , this.onClick, this);
3790 this.el.on("mouseover", this.onMouseOver, this);
3791 this.el.on("mouseout", this.onMouseOut, this);
3795 findTargetItem : function(e)
3797 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3801 //Roo.log(t); Roo.log(t.id);
3803 //Roo.log(this.menuitems);
3804 return this.menuitems.get(t.id);
3806 //return this.items.get(t.menuItemId);
3812 onTouch : function(e)
3814 Roo.log("menu.onTouch");
3815 //e.stopEvent(); this make the user popdown broken
3819 onClick : function(e)
3821 Roo.log("menu.onClick");
3823 var t = this.findTargetItem(e);
3824 if(!t || t.isContainer){
3829 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3830 if(t == this.activeItem && t.shouldDeactivate(e)){
3831 this.activeItem.deactivate();
3832 delete this.activeItem;
3836 this.setActiveItem(t, true);
3844 Roo.log('pass click event');
3848 this.fireEvent("click", this, t, e);
3852 if(!t.href.length || t.href == '#'){
3853 (function() { _this.hide(); }).defer(100);
3858 onMouseOver : function(e){
3859 var t = this.findTargetItem(e);
3862 // if(t.canActivate && !t.disabled){
3863 // this.setActiveItem(t, true);
3867 this.fireEvent("mouseover", this, e, t);
3869 isVisible : function(){
3870 return !this.hidden;
3872 onMouseOut : function(e){
3873 var t = this.findTargetItem(e);
3876 // if(t == this.activeItem && t.shouldDeactivate(e)){
3877 // this.activeItem.deactivate();
3878 // delete this.activeItem;
3881 this.fireEvent("mouseout", this, e, t);
3886 * Displays this menu relative to another element
3887 * @param {String/HTMLElement/Roo.Element} element The element to align to
3888 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3889 * the element (defaults to this.defaultAlign)
3890 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3892 show : function(el, pos, parentMenu)
3894 if (false === this.fireEvent("beforeshow", this)) {
3895 Roo.log("show canceled");
3898 this.parentMenu = parentMenu;
3902 this.el.addClass('show'); // show otherwise we do not know how big we are..
3904 var xy = this.el.getAlignToXY(el, pos);
3906 // bl-tl << left align below
3907 // tl-bl << left align
3909 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3910 // if it goes to far to the right.. -> align left.
3911 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3914 // was left align - go right?
3915 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3918 // goes down the bottom
3919 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3921 var a = this.align.replace('?', '').split('-');
3922 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3926 this.showAt( xy , parentMenu, false);
3929 * Displays this menu at a specific xy position
3930 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3931 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3933 showAt : function(xy, parentMenu, /* private: */_e){
3934 this.parentMenu = parentMenu;
3939 this.fireEvent("beforeshow", this);
3940 //xy = this.el.adjustForConstraints(xy);
3944 this.hideMenuItems();
3945 this.hidden = false;
3946 if (this.triggerEl) {
3947 this.triggerEl.addClass('open');
3950 this.el.addClass('show');
3954 // reassign x when hitting right
3956 // reassign y when hitting bottom
3958 // but the list may align on trigger left or trigger top... should it be a properity?
3960 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3965 this.fireEvent("show", this);
3971 this.doFocus.defer(50, this);
3975 doFocus : function(){
3977 this.focusEl.focus();
3982 * Hides this menu and optionally all parent menus
3983 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3985 hide : function(deep)
3987 if (false === this.fireEvent("beforehide", this)) {
3988 Roo.log("hide canceled");
3991 this.hideMenuItems();
3992 if(this.el && this.isVisible()){
3994 if(this.activeItem){
3995 this.activeItem.deactivate();
3996 this.activeItem = null;
3998 if (this.triggerEl) {
3999 this.triggerEl.removeClass('open');
4002 this.el.removeClass('show');
4004 this.fireEvent("hide", this);
4006 if(deep === true && this.parentMenu){
4007 this.parentMenu.hide(true);
4011 onTriggerClick : function(e)
4013 Roo.log('trigger click');
4015 var target = e.getTarget();
4017 Roo.log(target.nodeName.toLowerCase());
4019 if(target.nodeName.toLowerCase() === 'i'){
4025 onTriggerPress : function(e)
4027 Roo.log('trigger press');
4028 //Roo.log(e.getTarget());
4029 // Roo.log(this.triggerEl.dom);
4031 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4032 var pel = Roo.get(e.getTarget());
4033 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4034 Roo.log('is treeview or dropdown?');
4038 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4042 if (this.isVisible()) {
4048 this.show(this.triggerEl, this.align, false);
4051 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4058 hideMenuItems : function()
4060 Roo.log("hide Menu Items");
4065 this.el.select('.open',true).each(function(aa) {
4067 aa.removeClass('open');
4071 addxtypeChild : function (tree, cntr) {
4072 var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4074 this.menuitems.add(comp);
4086 this.getEl().dom.innerHTML = '';
4087 this.menuitems.clear();
4093 * @class Roo.bootstrap.menu.Item
4094 * @extends Roo.bootstrap.Component
4095 * @children Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4096 * @parent Roo.bootstrap.menu.Menu
4098 * Bootstrap MenuItem class
4100 * @cfg {String} html the menu label
4101 * @cfg {String} href the link
4102 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4103 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4104 * @cfg {Boolean} active used on sidebars to highlight active itesm
4105 * @cfg {String} fa favicon to show on left of menu item.
4106 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4110 * Create a new MenuItem
4111 * @param {Object} config The config object
4115 Roo.bootstrap.menu.Item = function(config){
4116 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4121 * The raw click event for the entire grid.
4122 * @param {Roo.bootstrap.menu.Item} this
4123 * @param {Roo.EventObject} e
4129 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
4133 preventDefault: false,
4134 isContainer : false,
4138 getAutoCreate : function(){
4140 if(this.isContainer){
4143 cls: 'dropdown-menu-item '
4153 cls : 'dropdown-item',
4158 if (this.fa !== false) {
4161 cls : 'fa fa-' + this.fa
4170 cls: 'dropdown-menu-item',
4173 if (this.parent().type == 'treeview') {
4174 cfg.cls = 'treeview-menu';
4177 cfg.cls += ' active';
4182 anc.href = this.href || cfg.cn[0].href ;
4183 ctag.html = this.html || cfg.cn[0].html ;
4187 initEvents: function()
4189 if (this.parent().type == 'treeview') {
4190 this.el.select('a').on('click', this.onClick, this);
4194 this.menu.parentType = this.xtype;
4195 this.menu.triggerEl = this.el;
4196 this.menu = this.addxtype(Roo.apply({}, this.menu));
4200 onClick : function(e)
4202 //Roo.log('item on click ');
4204 if(this.href === false || this.preventDefault){
4207 //this.parent().hideMenuItems();
4209 this.fireEvent('click', this, e);
4223 * @class Roo.bootstrap.menu.Separator
4224 * @extends Roo.bootstrap.Component
4226 * @parent Roo.bootstrap.menu.Menu
4227 * Bootstrap Separator class
4230 * Create a new Separator
4231 * @param {Object} config The config object
4235 Roo.bootstrap.menu.Separator = function(config){
4236 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4239 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
4241 getAutoCreate : function(){
4244 cls: 'dropdown-divider divider'
4260 * @class Roo.bootstrap.Modal
4261 * @extends Roo.bootstrap.Component
4262 * @parent none builder
4263 * @children Roo.bootstrap.Component
4264 * Bootstrap Modal class
4265 * @cfg {String} title Title of dialog
4266 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4267 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4268 * @cfg {Boolean} specificTitle default false
4269 * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4270 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4271 * @cfg {Boolean} animate default true
4272 * @cfg {Boolean} allow_close default true
4273 * @cfg {Boolean} fitwindow default false
4274 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4275 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4276 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4277 * @cfg {String} size (sm|lg|xl) default empty
4278 * @cfg {Number} max_width set the max width of modal
4279 * @cfg {Boolean} editableTitle can the title be edited
4284 * Create a new Modal Dialog
4285 * @param {Object} config The config object
4288 Roo.bootstrap.Modal = function(config){
4289 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4294 * The raw btnclick event for the button
4295 * @param {Roo.EventObject} e
4300 * Fire when dialog resize
4301 * @param {Roo.bootstrap.Modal} this
4302 * @param {Roo.EventObject} e
4306 * @event titlechanged
4307 * Fire when the editable title has been changed
4308 * @param {Roo.bootstrap.Modal} this
4309 * @param {Roo.EventObject} value
4311 "titlechanged" : true
4314 this.buttons = this.buttons || [];
4317 this.tmpl = Roo.factory(this.tmpl);
4322 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4324 title : 'test dialog',
4334 specificTitle: false,
4336 buttonPosition: 'right',
4358 editableTitle : false,
4360 onRender : function(ct, position)
4362 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4365 var cfg = Roo.apply({}, this.getAutoCreate());
4368 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4370 //if (!cfg.name.length) {
4374 cfg.cls += ' ' + this.cls;
4377 cfg.style = this.style;
4379 this.el = Roo.get(document.body).createChild(cfg, position);
4381 //var type = this.el.dom.type;
4384 if(this.tabIndex !== undefined){
4385 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4388 this.dialogEl = this.el.select('.modal-dialog',true).first();
4389 this.bodyEl = this.el.select('.modal-body',true).first();
4390 this.closeEl = this.el.select('.modal-header .close', true).first();
4391 this.headerEl = this.el.select('.modal-header',true).first();
4392 this.titleEl = this.el.select('.modal-title',true).first();
4393 this.footerEl = this.el.select('.modal-footer',true).first();
4395 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4397 //this.el.addClass("x-dlg-modal");
4399 if (this.buttons.length) {
4400 Roo.each(this.buttons, function(bb) {
4401 var b = Roo.apply({}, bb);
4402 b.xns = b.xns || Roo.bootstrap;
4403 b.xtype = b.xtype || 'Button';
4404 if (typeof(b.listeners) == 'undefined') {
4405 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4408 var btn = Roo.factory(b);
4410 btn.render(this.getButtonContainer());
4414 // render the children.
4417 if(typeof(this.items) != 'undefined'){
4418 var items = this.items;
4421 for(var i =0;i < items.length;i++) {
4422 // we force children not to montor widnow resize - as we do that for them.
4423 items[i].monitorWindowResize = false;
4424 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4428 this.items = nitems;
4430 // where are these used - they used to be body/close/footer
4434 //this.el.addClass([this.fieldClass, this.cls]);
4438 getAutoCreate : function()
4440 // we will default to modal-body-overflow - might need to remove or make optional later.
4442 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4443 html : this.html || ''
4448 cls : 'modal-title',
4452 if(this.specificTitle){ // WTF is this?
4457 if (this.allow_close && Roo.bootstrap.version == 3) {
4467 if (this.editableTitle) {
4469 cls: 'form-control roo-editable-title d-none',
4475 if (this.allow_close && Roo.bootstrap.version == 4) {
4485 if(this.size.length){
4486 size = 'modal-' + this.size;
4489 var footer = Roo.bootstrap.version == 3 ?
4491 cls : 'modal-footer',
4495 cls: 'btn-' + this.buttonPosition
4500 { // BS4 uses mr-auto on left buttons....
4501 cls : 'modal-footer'
4512 cls: "modal-dialog " + size,
4515 cls : "modal-content",
4518 cls : 'modal-header',
4533 modal.cls += ' fade';
4539 getChildContainer : function() {
4544 getButtonContainer : function() {
4546 return Roo.bootstrap.version == 4 ?
4547 this.el.select('.modal-footer',true).first()
4548 : this.el.select('.modal-footer div',true).first();
4552 closeClick : function()
4557 initEvents : function()
4559 if (this.allow_close) {
4560 this.closeEl.on('click', this.closeClick, this);
4562 Roo.EventManager.onWindowResize(this.resize, this, true);
4563 if (this.editableTitle) {
4564 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4565 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4566 this.headerEditEl.on('keyup', function(e) {
4567 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4568 this.toggleHeaderInput(false)
4571 this.headerEditEl.on('blur', function(e) {
4572 this.toggleHeaderInput(false)
4581 this.maskEl.setSize(
4582 Roo.lib.Dom.getViewWidth(true),
4583 Roo.lib.Dom.getViewHeight(true)
4586 if (this.fitwindow) {
4588 this.dialogEl.setStyle( { 'max-width' : '100%' });
4590 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4591 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4596 if(this.max_width !== 0) {
4598 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4601 this.setSize(w, this.height);
4605 if(this.max_height) {
4606 this.setSize(w,Math.min(
4608 Roo.lib.Dom.getViewportHeight(true) - 60
4614 if(!this.fit_content) {
4615 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4619 this.setSize(w, Math.min(
4621 this.headerEl.getHeight() +
4622 this.footerEl.getHeight() +
4623 this.getChildHeight(this.bodyEl.dom.childNodes),
4624 Roo.lib.Dom.getViewportHeight(true) - 60)
4630 setSize : function(w,h)
4637 // any layout/border etc.. resize..
4639 this.items.forEach( function(e) {
4640 e.layout ? e.layout() : false;
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 is for children that are... layout.Border
4693 this.items.forEach( function(e) {
4694 e.layout ? e.layout() : false;
4702 if(this.fireEvent("beforehide", this) !== false){
4704 this.maskEl.removeClass('show');
4706 this.maskEl.dom.style.display = '';
4707 Roo.get(document.body).removeClass("x-body-masked");
4708 this.el.removeClass('in');
4709 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4711 if(this.animate){ // why
4712 this.el.addClass('hideing');
4713 this.el.removeClass('show');
4715 if (!this.el.hasClass('hideing')) {
4716 return; // it's been shown again...
4719 this.el.dom.style.display='';
4721 Roo.get(document.body).removeClass('modal-open');
4722 this.el.removeClass('hideing');
4726 this.el.removeClass('show');
4727 this.el.dom.style.display='';
4728 Roo.get(document.body).removeClass('modal-open');
4731 this.fireEvent('hide', this);
4734 isVisible : function()
4737 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4741 addButton : function(str, cb)
4745 var b = Roo.apply({}, { html : str } );
4746 b.xns = b.xns || Roo.bootstrap;
4747 b.xtype = b.xtype || 'Button';
4748 if (typeof(b.listeners) == 'undefined') {
4749 b.listeners = { click : cb.createDelegate(this) };
4752 var btn = Roo.factory(b);
4754 btn.render(this.getButtonContainer());
4760 setDefaultButton : function(btn)
4762 //this.el.select('.modal-footer').()
4765 resizeTo: function(w,h)
4767 this.dialogEl.setWidth(w);
4769 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4771 this.bodyEl.setHeight(h - diff);
4773 this.fireEvent('resize', this);
4776 setContentSize : function(w, h)
4780 onButtonClick: function(btn,e)
4783 this.fireEvent('btnclick', btn.name, e);
4786 * Set the title of the Dialog
4787 * @param {String} str new Title
4789 setTitle: function(str) {
4790 this.titleEl.dom.innerHTML = str;
4794 * Set the body of the Dialog
4795 * @param {String} str new Title
4797 setBody: function(str) {
4798 this.bodyEl.dom.innerHTML = str;
4801 * Set the body of the Dialog using the template
4802 * @param {Obj} data - apply this data to the template and replace the body contents.
4804 applyBody: function(obj)
4807 Roo.log("Error - using apply Body without a template");
4810 this.tmpl.overwrite(this.bodyEl, obj);
4813 getChildHeight : function(child_nodes)
4817 child_nodes.length == 0
4822 var child_height = 0;
4824 for(var i = 0; i < child_nodes.length; i++) {
4827 * for modal with tabs...
4828 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4830 var layout_childs = child_nodes[i].childNodes;
4832 for(var j = 0; j < layout_childs.length; j++) {
4834 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4836 var layout_body_childs = layout_childs[j].childNodes;
4838 for(var k = 0; k < layout_body_childs.length; k++) {
4840 if(layout_body_childs[k].classList.contains('navbar')) {
4841 child_height += layout_body_childs[k].offsetHeight;
4845 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4847 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4849 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4851 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4852 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4867 child_height += child_nodes[i].offsetHeight;
4868 // Roo.log(child_nodes[i].offsetHeight);
4871 return child_height;
4873 toggleHeaderInput : function(is_edit)
4875 if (!this.editableTitle) {
4876 return; // not editable.
4878 if (is_edit && this.is_header_editing) {
4879 return; // already editing..
4883 this.headerEditEl.dom.value = this.title;
4884 this.headerEditEl.removeClass('d-none');
4885 this.headerEditEl.dom.focus();
4886 this.titleEl.addClass('d-none');
4888 this.is_header_editing = true;
4891 // flip back to not editing.
4892 this.title = this.headerEditEl.dom.value;
4893 this.headerEditEl.addClass('d-none');
4894 this.titleEl.removeClass('d-none');
4895 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4896 this.is_header_editing = false;
4897 this.fireEvent('titlechanged', this, this.title);
4906 Roo.apply(Roo.bootstrap.Modal, {
4908 * Button config that displays a single OK button
4917 * Button config that displays Yes and No buttons
4933 * Button config that displays OK and Cancel buttons
4948 * Button config that displays Yes, No and Cancel buttons
4973 * messagebox - can be used as a replace
4977 * @class Roo.MessageBox
4978 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4982 Roo.Msg.alert('Status', 'Changes saved successfully.');
4984 // Prompt for user data:
4985 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4987 // process text value...
4991 // Show a dialog using config options:
4993 title:'Save Changes?',
4994 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4995 buttons: Roo.Msg.YESNOCANCEL,
5002 Roo.bootstrap.MessageBox = function(){
5003 var dlg, opt, mask, waitTimer;
5004 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
5005 var buttons, activeTextEl, bwidth;
5009 var handleButton = function(button){
5011 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5015 var handleHide = function(){
5017 dlg.el.removeClass(opt.cls);
5020 // Roo.TaskMgr.stop(waitTimer);
5021 // waitTimer = null;
5026 var updateButtons = function(b){
5029 buttons["ok"].hide();
5030 buttons["cancel"].hide();
5031 buttons["yes"].hide();
5032 buttons["no"].hide();
5033 dlg.footerEl.hide();
5037 dlg.footerEl.show();
5038 for(var k in buttons){
5039 if(typeof buttons[k] != "function"){
5042 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5043 width += buttons[k].el.getWidth()+15;
5053 var handleEsc = function(d, k, e){
5054 if(opt && opt.closable !== false){
5064 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5065 * @return {Roo.BasicDialog} The BasicDialog element
5067 getDialog : function(){
5069 dlg = new Roo.bootstrap.Modal( {
5072 //constraintoviewport:false,
5074 //collapsible : false,
5079 //buttonAlign:"center",
5080 closeClick : function(){
5081 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5084 handleButton("cancel");
5089 dlg.on("hide", handleHide);
5091 //dlg.addKeyListener(27, handleEsc);
5093 this.buttons = buttons;
5094 var bt = this.buttonText;
5095 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5096 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5097 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5098 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5100 bodyEl = dlg.bodyEl.createChild({
5102 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5103 '<textarea class="roo-mb-textarea"></textarea>' +
5104 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5106 msgEl = bodyEl.dom.firstChild;
5107 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5108 textboxEl.enableDisplayMode();
5109 textboxEl.addKeyListener([10,13], function(){
5110 if(dlg.isVisible() && opt && opt.buttons){
5113 }else if(opt.buttons.yes){
5114 handleButton("yes");
5118 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5119 textareaEl.enableDisplayMode();
5120 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5121 progressEl.enableDisplayMode();
5123 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5124 var pf = progressEl.dom.firstChild;
5126 pp = Roo.get(pf.firstChild);
5127 pp.setHeight(pf.offsetHeight);
5135 * Updates the message box body text
5136 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5137 * the XHTML-compliant non-breaking space character '&#160;')
5138 * @return {Roo.MessageBox} This message box
5140 updateText : function(text)
5142 if(!dlg.isVisible() && !opt.width){
5143 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5144 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5146 msgEl.innerHTML = text || ' ';
5148 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5149 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5151 Math.min(opt.width || cw , this.maxWidth),
5152 Math.max(opt.minWidth || this.minWidth, bwidth)
5155 activeTextEl.setWidth(w);
5157 if(dlg.isVisible()){
5158 dlg.fixedcenter = false;
5160 // to big, make it scroll. = But as usual stupid IE does not support
5163 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5164 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5165 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5167 bodyEl.dom.style.height = '';
5168 bodyEl.dom.style.overflowY = '';
5171 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5173 bodyEl.dom.style.overflowX = '';
5176 dlg.setContentSize(w, bodyEl.getHeight());
5177 if(dlg.isVisible()){
5178 dlg.fixedcenter = true;
5184 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5185 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5186 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5187 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5188 * @return {Roo.MessageBox} This message box
5190 updateProgress : function(value, text){
5192 this.updateText(text);
5195 if (pp) { // weird bug on my firefox - for some reason this is not defined
5196 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5197 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5203 * Returns true if the message box is currently displayed
5204 * @return {Boolean} True if the message box is visible, else false
5206 isVisible : function(){
5207 return dlg && dlg.isVisible();
5211 * Hides the message box if it is displayed
5214 if(this.isVisible()){
5220 * Displays a new message box, or reinitializes an existing message box, based on the config options
5221 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5222 * The following config object properties are supported:
5224 Property Type Description
5225 ---------- --------------- ------------------------------------------------------------------------------------
5226 animEl String/Element An id or Element from which the message box should animate as it opens and
5227 closes (defaults to undefined)
5228 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5229 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5230 closable Boolean False to hide the top-right close button (defaults to true). Note that
5231 progress and wait dialogs will ignore this property and always hide the
5232 close button as they can only be closed programmatically.
5233 cls String A custom CSS class to apply to the message box element
5234 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5235 displayed (defaults to 75)
5236 fn Function A callback function to execute after closing the dialog. The arguments to the
5237 function will be btn (the name of the button that was clicked, if applicable,
5238 e.g. "ok"), and text (the value of the active text field, if applicable).
5239 Progress and wait dialogs will ignore this option since they do not respond to
5240 user actions and can only be closed programmatically, so any required function
5241 should be called by the same code after it closes the dialog.
5242 icon String A CSS class that provides a background image to be used as an icon for
5243 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5244 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5245 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5246 modal Boolean False to allow user interaction with the page while the message box is
5247 displayed (defaults to true)
5248 msg String A string that will replace the existing message box body text (defaults
5249 to the XHTML-compliant non-breaking space character ' ')
5250 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5251 progress Boolean True to display a progress bar (defaults to false)
5252 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5253 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5254 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5255 title String The title text
5256 value String The string value to set into the active textbox element if displayed
5257 wait Boolean True to display a progress bar (defaults to false)
5258 width Number The width of the dialog in pixels
5265 msg: 'Please enter your address:',
5267 buttons: Roo.MessageBox.OKCANCEL,
5270 animEl: 'addAddressBtn'
5273 * @param {Object} config Configuration options
5274 * @return {Roo.MessageBox} This message box
5276 show : function(options)
5279 // this causes nightmares if you show one dialog after another
5280 // especially on callbacks..
5282 if(this.isVisible()){
5285 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5286 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5287 Roo.log("New Dialog Message:" + options.msg )
5288 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5289 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5292 var d = this.getDialog();
5294 d.setTitle(opt.title || " ");
5295 d.closeEl.setDisplayed(opt.closable !== false);
5296 activeTextEl = textboxEl;
5297 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5302 textareaEl.setHeight(typeof opt.multiline == "number" ?
5303 opt.multiline : this.defaultTextHeight);
5304 activeTextEl = textareaEl;
5313 progressEl.setDisplayed(opt.progress === true);
5315 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5317 this.updateProgress(0);
5318 activeTextEl.dom.value = opt.value || "";
5320 dlg.setDefaultButton(activeTextEl);
5322 var bs = opt.buttons;
5326 }else if(bs && bs.yes){
5327 db = buttons["yes"];
5329 dlg.setDefaultButton(db);
5331 bwidth = updateButtons(opt.buttons);
5332 this.updateText(opt.msg);
5334 d.el.addClass(opt.cls);
5336 d.proxyDrag = opt.proxyDrag === true;
5337 d.modal = opt.modal !== false;
5338 d.mask = opt.modal !== false ? mask : false;
5340 // force it to the end of the z-index stack so it gets a cursor in FF
5341 document.body.appendChild(dlg.el.dom);
5342 d.animateTarget = null;
5343 d.show(options.animEl);
5349 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5350 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5351 * and closing the message box when the process is complete.
5352 * @param {String} title The title bar text
5353 * @param {String} msg The message box body text
5354 * @return {Roo.MessageBox} This message box
5356 progress : function(title, msg){
5363 minWidth: this.minProgressWidth,
5370 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5371 * If a callback function is passed it will be called after the user clicks the button, and the
5372 * id of the button that was clicked will be passed as the only parameter to the callback
5373 * (could also be the top-right close button).
5374 * @param {String} title The title bar text
5375 * @param {String} msg The message box body text
5376 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5377 * @param {Object} scope (optional) The scope of the callback function
5378 * @return {Roo.MessageBox} This message box
5380 alert : function(title, msg, fn, scope)
5395 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5396 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5397 * You are responsible for closing the message box when the process is complete.
5398 * @param {String} msg The message box body text
5399 * @param {String} title (optional) The title bar text
5400 * @return {Roo.MessageBox} This message box
5402 wait : function(msg, title){
5413 waitTimer = Roo.TaskMgr.start({
5415 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5423 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5424 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5425 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5426 * @param {String} title The title bar text
5427 * @param {String} msg The message box body text
5428 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5429 * @param {Object} scope (optional) The scope of the callback function
5430 * @return {Roo.MessageBox} This message box
5432 confirm : function(title, msg, fn, scope){
5436 buttons: this.YESNO,
5445 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5446 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5447 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5448 * (could also be the top-right close button) and the text that was entered will be passed as the two
5449 * parameters to the callback.
5450 * @param {String} title The title bar text
5451 * @param {String} msg The message box body text
5452 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5453 * @param {Object} scope (optional) The scope of the callback function
5454 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5455 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5456 * @return {Roo.MessageBox} This message box
5458 prompt : function(title, msg, fn, scope, multiline){
5462 buttons: this.OKCANCEL,
5467 multiline: multiline,
5474 * Button config that displays a single OK button
5479 * Button config that displays Yes and No buttons
5482 YESNO : {yes:true, no:true},
5484 * Button config that displays OK and Cancel buttons
5487 OKCANCEL : {ok:true, cancel:true},
5489 * Button config that displays Yes, No and Cancel buttons
5492 YESNOCANCEL : {yes:true, no:true, cancel:true},
5495 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5498 defaultTextHeight : 75,
5500 * The maximum width in pixels of the message box (defaults to 600)
5505 * The minimum width in pixels of the message box (defaults to 100)
5510 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5511 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5514 minProgressWidth : 250,
5516 * An object containing the default button text strings that can be overriden for localized language support.
5517 * Supported properties are: ok, cancel, yes and no.
5518 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5531 * Shorthand for {@link Roo.MessageBox}
5533 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5534 Roo.Msg = Roo.Msg || Roo.MessageBox;
5543 * @class Roo.bootstrap.nav.Bar
5544 * @extends Roo.bootstrap.Component
5546 * Bootstrap Navbar class
5549 * Create a new Navbar
5550 * @param {Object} config The config object
5554 Roo.bootstrap.nav.Bar = function(config){
5555 Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5559 * @event beforetoggle
5560 * Fire before toggle the menu
5561 * @param {Roo.EventObject} e
5563 "beforetoggle" : true
5567 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component, {
5576 getAutoCreate : function(){
5579 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5583 initEvents :function ()
5585 //Roo.log(this.el.select('.navbar-toggle',true));
5586 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5593 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5595 var size = this.el.getSize();
5596 this.maskEl.setSize(size.width, size.height);
5597 this.maskEl.enableDisplayMode("block");
5606 getChildContainer : function()
5608 if (this.el && this.el.select('.collapse').getCount()) {
5609 return this.el.select('.collapse',true).first();
5624 onToggle : function()
5627 if(this.fireEvent('beforetoggle', this) === false){
5630 var ce = this.el.select('.navbar-collapse',true).first();
5632 if (!ce.hasClass('show')) {
5642 * Expand the navbar pulldown
5644 expand : function ()
5647 var ce = this.el.select('.navbar-collapse',true).first();
5648 if (ce.hasClass('collapsing')) {
5651 ce.dom.style.height = '';
5653 ce.addClass('in'); // old...
5654 ce.removeClass('collapse');
5655 ce.addClass('show');
5656 var h = ce.getHeight();
5658 ce.removeClass('show');
5659 // at this point we should be able to see it..
5660 ce.addClass('collapsing');
5662 ce.setHeight(0); // resize it ...
5663 ce.on('transitionend', function() {
5664 //Roo.log('done transition');
5665 ce.removeClass('collapsing');
5666 ce.addClass('show');
5667 ce.removeClass('collapse');
5669 ce.dom.style.height = '';
5670 }, this, { single: true} );
5672 ce.dom.scrollTop = 0;
5675 * Collapse the navbar pulldown
5677 collapse : function()
5679 var ce = this.el.select('.navbar-collapse',true).first();
5681 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5682 // it's collapsed or collapsing..
5685 ce.removeClass('in'); // old...
5686 ce.setHeight(ce.getHeight());
5687 ce.removeClass('show');
5688 ce.addClass('collapsing');
5690 ce.on('transitionend', function() {
5691 ce.dom.style.height = '';
5692 ce.removeClass('collapsing');
5693 ce.addClass('collapse');
5694 }, this, { single: true} );
5714 * @class Roo.bootstrap.nav.Simplebar
5715 * @extends Roo.bootstrap.nav.Bar
5716 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5717 * Bootstrap Sidebar class
5719 * @cfg {Boolean} inverse is inverted color
5721 * @cfg {String} type (nav | pills | tabs)
5722 * @cfg {Boolean} arrangement stacked | justified
5723 * @cfg {String} align (left | right) alignment
5725 * @cfg {Boolean} main (true|false) main nav bar? default false
5726 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5728 * @cfg {String} tag (header|footer|nav|div) default is nav
5730 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5734 * Create a new Sidebar
5735 * @param {Object} config The config object
5739 Roo.bootstrap.nav.Simplebar = function(config){
5740 Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5743 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar, {
5759 getAutoCreate : function(){
5763 tag : this.tag || 'div',
5764 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5766 if (['light','white'].indexOf(this.weight) > -1) {
5767 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5769 cfg.cls += ' bg-' + this.weight;
5772 cfg.cls += ' navbar-inverse';
5776 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5778 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5787 cls: 'nav nav-' + this.xtype,
5793 this.type = this.type || 'nav';
5794 if (['tabs','pills'].indexOf(this.type) != -1) {
5795 cfg.cn[0].cls += ' nav-' + this.type
5799 if (this.type!=='nav') {
5800 Roo.log('nav type must be nav/tabs/pills')
5802 cfg.cn[0].cls += ' navbar-nav'
5808 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5809 cfg.cn[0].cls += ' nav-' + this.arrangement;
5813 if (this.align === 'right') {
5814 cfg.cn[0].cls += ' navbar-right';
5839 * navbar-expand-md fixed-top
5843 * @class Roo.bootstrap.nav.Headerbar
5844 * @extends Roo.bootstrap.nav.Simplebar
5845 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5846 * Bootstrap Sidebar class
5848 * @cfg {String} brand what is brand
5849 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5850 * @cfg {String} brand_href href of the brand
5851 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5852 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5853 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5854 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5857 * Create a new Sidebar
5858 * @param {Object} config The config object
5862 Roo.bootstrap.nav.Headerbar = function(config){
5863 Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5867 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar, {
5874 desktopCenter : false,
5877 getAutoCreate : function(){
5880 tag: this.nav || 'nav',
5881 cls: 'navbar navbar-expand-md',
5887 if (this.desktopCenter) {
5888 cn.push({cls : 'container', cn : []});
5896 cls: 'navbar-toggle navbar-toggler',
5897 'data-toggle': 'collapse',
5902 html: 'Toggle navigation'
5906 cls: 'icon-bar navbar-toggler-icon'
5919 cn.push( Roo.bootstrap.version == 4 ? btn : {
5921 cls: 'navbar-header',
5930 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5934 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5936 if (['light','white'].indexOf(this.weight) > -1) {
5937 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5939 cfg.cls += ' bg-' + this.weight;
5942 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5943 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5945 // tag can override this..
5947 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5950 if (this.brand !== '') {
5951 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5952 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5954 href: this.brand_href ? this.brand_href : '#',
5955 cls: 'navbar-brand',
5963 cfg.cls += ' main-nav';
5971 getHeaderChildContainer : function()
5973 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5974 return this.el.select('.navbar-header',true).first();
5977 return this.getChildContainer();
5980 getChildContainer : function()
5983 return this.el.select('.roo-navbar-collapse',true).first();
5988 initEvents : function()
5990 Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5992 if (this.autohide) {
5997 Roo.get(document).on('scroll',function(e) {
5998 var ns = Roo.get(document).getScroll().top;
5999 var os = prevScroll;
6003 ft.removeClass('slideDown');
6004 ft.addClass('slideUp');
6007 ft.removeClass('slideUp');
6008 ft.addClass('slideDown');
6029 * @class Roo.bootstrap.nav.Sidebar
6030 * @extends Roo.bootstrap.nav.Bar
6031 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6032 * Bootstrap Sidebar class
6035 * Create a new Sidebar
6036 * @param {Object} config The config object
6040 Roo.bootstrap.nav.Sidebar = function(config){
6041 Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6044 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar, {
6046 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6048 getAutoCreate : function(){
6053 cls: 'sidebar sidebar-nav'
6075 * @class Roo.bootstrap.nav.Group
6076 * @extends Roo.bootstrap.Component
6077 * @children Roo.bootstrap.nav.Item
6078 * Bootstrap NavGroup class
6079 * @cfg {String} align (left|right)
6080 * @cfg {Boolean} inverse
6081 * @cfg {String} type (nav|pills|tab) default nav
6082 * @cfg {String} navId - reference Id for navbar.
6083 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6086 * Create a new nav group
6087 * @param {Object} config The config object
6090 Roo.bootstrap.nav.Group = function(config){
6091 Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6094 Roo.bootstrap.nav.Group.register(this);
6098 * Fires when the active item changes
6099 * @param {Roo.bootstrap.nav.Group} this
6100 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6101 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6108 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component, {
6120 getAutoCreate : function()
6122 var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6128 if (Roo.bootstrap.version == 4) {
6129 if (['tabs','pills'].indexOf(this.type) != -1) {
6130 cfg.cls += ' nav-' + this.type;
6132 // trying to remove so header bar can right align top?
6133 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6134 // do not use on header bar...
6135 cfg.cls += ' navbar-nav';
6140 if (['tabs','pills'].indexOf(this.type) != -1) {
6141 cfg.cls += ' nav-' + this.type
6143 if (this.type !== 'nav') {
6144 Roo.log('nav type must be nav/tabs/pills')
6146 cfg.cls += ' navbar-nav'
6150 if (this.parent() && this.parent().sidebar) {
6153 cls: 'dashboard-menu sidebar-menu'
6159 if (this.form === true) {
6162 cls: 'navbar-form form-inline'
6164 //nav navbar-right ml-md-auto
6165 if (this.align === 'right') {
6166 cfg.cls += ' navbar-right ml-md-auto';
6168 cfg.cls += ' navbar-left';
6172 if (this.align === 'right') {
6173 cfg.cls += ' navbar-right ml-md-auto';
6175 cfg.cls += ' mr-auto';
6179 cfg.cls += ' navbar-inverse';
6187 * sets the active Navigation item
6188 * @param {Roo.bootstrap.nav.Item} the new current navitem
6190 setActiveItem : function(item)
6193 Roo.each(this.navItems, function(v){
6198 v.setActive(false, true);
6205 item.setActive(true, true);
6206 this.fireEvent('changed', this, item, prev);
6211 * gets the active Navigation item
6212 * @return {Roo.bootstrap.nav.Item} the current navitem
6214 getActive : function()
6218 Roo.each(this.navItems, function(v){
6229 indexOfNav : function()
6233 Roo.each(this.navItems, function(v,i){
6244 * adds a Navigation item
6245 * @param {Roo.bootstrap.nav.Item} the navitem to add
6247 addItem : function(cfg)
6249 if (this.form && Roo.bootstrap.version == 4) {
6252 var cn = new Roo.bootstrap.nav.Item(cfg);
6254 cn.parentId = this.id;
6255 cn.onRender(this.el, null);
6259 * register a Navigation item
6260 * @param {Roo.bootstrap.nav.Item} the navitem to add
6262 register : function(item)
6264 this.navItems.push( item);
6265 item.navId = this.navId;
6270 * clear all the Navigation item
6273 clearAll : function()
6276 this.el.dom.innerHTML = '';
6279 getNavItem: function(tabId)
6282 Roo.each(this.navItems, function(e) {
6283 if (e.tabId == tabId) {
6293 setActiveNext : function()
6295 var i = this.indexOfNav(this.getActive());
6296 if (i > this.navItems.length) {
6299 this.setActiveItem(this.navItems[i+1]);
6301 setActivePrev : function()
6303 var i = this.indexOfNav(this.getActive());
6307 this.setActiveItem(this.navItems[i-1]);
6309 clearWasActive : function(except) {
6310 Roo.each(this.navItems, function(e) {
6311 if (e.tabId != except.tabId && e.was_active) {
6312 e.was_active = false;
6319 getWasActive : function ()
6322 Roo.each(this.navItems, function(e) {
6337 Roo.apply(Roo.bootstrap.nav.Group, {
6341 * register a Navigation Group
6342 * @param {Roo.bootstrap.nav.Group} the navgroup to add
6344 register : function(navgrp)
6346 this.groups[navgrp.navId] = navgrp;
6350 * fetch a Navigation Group based on the navigation ID
6351 * @param {string} the navgroup to add
6352 * @returns {Roo.bootstrap.nav.Group} the navgroup
6354 get: function(navId) {
6355 if (typeof(this.groups[navId]) == 'undefined') {
6357 //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6359 return this.groups[navId] ;
6367 * @class Roo.bootstrap.nav.Item
6368 * @extends Roo.bootstrap.Component
6369 * @children Roo.bootstrap.Container Roo.bootstrap.Button
6370 * @parent Roo.bootstrap.nav.Group
6372 * Bootstrap Navbar.NavItem class
6374 * @cfg {String} href link to
6375 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6376 * @cfg {Boolean} button_outline show and outlined button
6377 * @cfg {String} html content of button
6378 * @cfg {String} badge text inside badge
6379 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6380 * @cfg {String} glyphicon DEPRICATED - use fa
6381 * @cfg {String} icon DEPRICATED - use fa
6382 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6383 * @cfg {Boolean} active Is item active
6384 * @cfg {Boolean} disabled Is item disabled
6385 * @cfg {String} linkcls Link Class
6386 * @cfg {Boolean} preventDefault (true | false) default false
6387 * @cfg {String} tabId the tab that this item activates.
6388 * @cfg {String} tagtype (a|span) render as a href or span?
6389 * @cfg {Boolean} animateRef (true|false) link to element default false
6390 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
6393 * Create a new Navbar Item
6394 * @param {Object} config The config object
6396 Roo.bootstrap.nav.Item = function(config){
6397 Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6402 * The raw click event for the entire grid.
6403 * @param {Roo.EventObject} e
6408 * Fires when the active item active state changes
6409 * @param {Roo.bootstrap.nav.Item} this
6410 * @param {boolean} state the new state
6416 * Fires when scroll to element
6417 * @param {Roo.bootstrap.nav.Item} this
6418 * @param {Object} options
6419 * @param {Roo.EventObject} e
6427 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component, {
6436 preventDefault : false,
6444 button_outline : false,
6448 getAutoCreate : function(){
6455 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6458 cfg.cls += ' active' ;
6460 if (this.disabled) {
6461 cfg.cls += ' disabled';
6465 if (this.button_weight.length) {
6466 cfg.tag = this.href ? 'a' : 'button';
6467 cfg.html = this.html || '';
6468 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6470 cfg.href = this.href;
6473 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6475 cfg.cls += " nav-html";
6478 // menu .. should add dropdown-menu class - so no need for carat..
6480 if (this.badge !== '') {
6482 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6487 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6491 href : this.href || "#",
6492 html: this.html || '',
6496 if (this.tagtype == 'a') {
6497 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6501 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6502 } else if (this.fa) {
6503 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6504 } else if(this.glyphicon) {
6505 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6507 cfg.cn[0].cls += " nav-html";
6511 cfg.cn[0].html += " <span class='caret'></span>";
6515 if (this.badge !== '') {
6516 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6524 onRender : function(ct, position)
6526 // Roo.log("Call onRender: " + this.xtype);
6527 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6531 var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6532 this.navLink = this.el.select('.nav-link',true).first();
6533 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6538 initEvents: function()
6540 if (typeof (this.menu) != 'undefined') {
6541 this.menu.parentType = this.xtype;
6542 this.menu.triggerEl = this.el;
6543 this.menu = this.addxtype(Roo.apply({}, this.menu));
6546 this.el.on('click', this.onClick, this);
6548 //if(this.tagtype == 'span'){
6549 // this.el.select('span',true).on('click', this.onClick, this);
6552 // at this point parent should be available..
6553 this.parent().register(this);
6556 onClick : function(e)
6558 if (e.getTarget('.dropdown-menu-item')) {
6559 // did you click on a menu itemm.... - then don't trigger onclick..
6564 this.preventDefault ||
6565 this.href === false ||
6568 //Roo.log("NavItem - prevent Default?");
6572 if (this.disabled) {
6576 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6577 if (tg && tg.transition) {
6578 Roo.log("waiting for the transitionend");
6584 //Roo.log("fire event clicked");
6585 if(this.fireEvent('click', this, e) === false){
6589 if(this.tagtype == 'span'){
6593 //Roo.log(this.href);
6594 var ael = this.el.select('a',true).first();
6597 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6598 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6599 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6600 return; // ignore... - it's a 'hash' to another page.
6602 Roo.log("NavItem - prevent Default?");
6604 this.scrollToElement(e);
6608 var p = this.parent();
6610 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6611 if (typeof(p.setActiveItem) !== 'undefined') {
6612 p.setActiveItem(this);
6616 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6617 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6618 // remove the collapsed menu expand...
6619 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6623 isActive: function () {
6626 setActive : function(state, fire, is_was_active)
6628 if (this.active && !state && this.navId) {
6629 this.was_active = true;
6630 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6632 nv.clearWasActive(this);
6636 this.active = state;
6639 this.el.removeClass('active');
6640 this.navLink ? this.navLink.removeClass('active') : false;
6641 } else if (!this.el.hasClass('active')) {
6643 this.el.addClass('active');
6644 if (Roo.bootstrap.version == 4 && this.navLink ) {
6645 this.navLink.addClass('active');
6650 this.fireEvent('changed', this, state);
6653 // show a panel if it's registered and related..
6655 if (!this.navId || !this.tabId || !state || is_was_active) {
6659 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6663 var pan = tg.getPanelByName(this.tabId);
6667 // if we can not flip to new panel - go back to old nav highlight..
6668 if (false == tg.showPanel(pan)) {
6669 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6671 var onav = nv.getWasActive();
6673 onav.setActive(true, false, true);
6682 // this should not be here...
6683 setDisabled : function(state)
6685 this.disabled = state;
6687 this.el.removeClass('disabled');
6688 } else if (!this.el.hasClass('disabled')) {
6689 this.el.addClass('disabled');
6695 * Fetch the element to display the tooltip on.
6696 * @return {Roo.Element} defaults to this.el
6698 tooltipEl : function()
6700 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6703 scrollToElement : function(e)
6705 var c = document.body;
6708 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6710 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6711 c = document.documentElement;
6714 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6720 var o = target.calcOffsetsTo(c);
6727 this.fireEvent('scrollto', this, options, e);
6729 Roo.get(c).scrollTo('top', options.value, true);
6734 * Set the HTML (text content) of the item
6735 * @param {string} html content for the nav item
6737 setHtml : function(html)
6740 this.htmlEl.dom.innerHTML = html;
6752 * <span> icon </span>
6753 * <span> text </span>
6754 * <span>badge </span>
6758 * @class Roo.bootstrap.nav.SidebarItem
6759 * @extends Roo.bootstrap.nav.Item
6760 * Bootstrap Navbar.NavSidebarItem class
6762 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6763 * {Boolean} open is the menu open
6764 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6765 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6766 * {String} buttonSize (sm|md|lg)the extra classes for the button
6767 * {Boolean} showArrow show arrow next to the text (default true)
6769 * Create a new Navbar Button
6770 * @param {Object} config The config object
6772 Roo.bootstrap.nav.SidebarItem = function(config){
6773 Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6778 * The raw click event for the entire grid.
6779 * @param {Roo.EventObject} e
6784 * Fires when the active item active state changes
6785 * @param {Roo.bootstrap.nav.SidebarItem} this
6786 * @param {boolean} state the new state
6794 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item, {
6796 badgeWeight : 'default',
6802 buttonWeight : 'default',
6808 getAutoCreate : function(){
6813 href : this.href || '#',
6819 if(this.buttonView){
6822 href : this.href || '#',
6823 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6836 cfg.cls += ' active';
6839 if (this.disabled) {
6840 cfg.cls += ' disabled';
6843 cfg.cls += ' open x-open';
6846 if (this.glyphicon || this.icon) {
6847 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6848 a.cn.push({ tag : 'i', cls : c }) ;
6851 if(!this.buttonView){
6854 html : this.html || ''
6861 if (this.badge !== '') {
6862 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6868 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6871 a.cls += ' dropdown-toggle treeview' ;
6877 initEvents : function()
6879 if (typeof (this.menu) != 'undefined') {
6880 this.menu.parentType = this.xtype;
6881 this.menu.triggerEl = this.el;
6882 this.menu = this.addxtype(Roo.apply({}, this.menu));
6885 this.el.on('click', this.onClick, this);
6887 if(this.badge !== ''){
6888 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6893 onClick : function(e)
6900 if(this.preventDefault){
6904 this.fireEvent('click', this, e);
6907 disable : function()
6909 this.setDisabled(true);
6914 this.setDisabled(false);
6917 setDisabled : function(state)
6919 if(this.disabled == state){
6923 this.disabled = state;
6926 this.el.addClass('disabled');
6930 this.el.removeClass('disabled');
6935 setActive : function(state)
6937 if(this.active == state){
6941 this.active = state;
6944 this.el.addClass('active');
6948 this.el.removeClass('active');
6953 isActive: function ()
6958 setBadge : function(str)
6964 this.badgeEl.dom.innerHTML = str;
6981 * @class Roo.bootstrap.nav.ProgressBar
6982 * @extends Roo.bootstrap.Component
6983 * @children Roo.bootstrap.nav.ProgressBarItem
6984 * Bootstrap NavProgressBar class
6987 * Create a new nav progress bar - a bar indicating step along a process
6988 * @param {Object} config The config object
6991 Roo.bootstrap.nav.ProgressBar = function(config){
6992 Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6994 this.bullets = this.bullets || [];
6996 // Roo.bootstrap.nav.ProgressBar.register(this);
7000 * Fires when the active item changes
7001 * @param {Roo.bootstrap.nav.ProgressBar} this
7002 * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
7003 * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item
7010 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component, {
7012 * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7013 * Bullets for the Nav Progress bar for the toolbar
7018 getAutoCreate : function()
7020 var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7024 cls : 'roo-navigation-bar-group',
7028 cls : 'roo-navigation-top-bar'
7032 cls : 'roo-navigation-bullets-bar',
7036 cls : 'roo-navigation-bar'
7043 cls : 'roo-navigation-bottom-bar'
7053 initEvents: function()
7058 onRender : function(ct, position)
7060 Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7062 if(this.bullets.length){
7063 Roo.each(this.bullets, function(b){
7072 addItem : function(cfg)
7074 var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7076 item.parentId = this.id;
7077 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7080 var top = new Roo.bootstrap.Element({
7082 cls : 'roo-navigation-bar-text'
7085 var bottom = new Roo.bootstrap.Element({
7087 cls : 'roo-navigation-bar-text'
7090 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7091 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7093 var topText = new Roo.bootstrap.Element({
7095 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7098 var bottomText = new Roo.bootstrap.Element({
7100 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7103 topText.onRender(top.el, null);
7104 bottomText.onRender(bottom.el, null);
7107 item.bottomEl = bottom;
7110 this.barItems.push(item);
7115 getActive : function()
7119 Roo.each(this.barItems, function(v){
7121 if (!v.isActive()) {
7133 setActiveItem : function(item)
7137 Roo.each(this.barItems, function(v){
7138 if (v.rid == item.rid) {
7148 item.setActive(true);
7150 this.fireEvent('changed', this, item, prev);
7153 getBarItem: function(rid)
7157 Roo.each(this.barItems, function(e) {
7169 indexOfItem : function(item)
7173 Roo.each(this.barItems, function(v, i){
7175 if (v.rid != item.rid) {
7186 setActiveNext : function()
7188 var i = this.indexOfItem(this.getActive());
7190 if (i > this.barItems.length) {
7194 this.setActiveItem(this.barItems[i+1]);
7197 setActivePrev : function()
7199 var i = this.indexOfItem(this.getActive());
7205 this.setActiveItem(this.barItems[i-1]);
7210 if(!this.barItems.length){
7214 var width = 100 / this.barItems.length;
7216 Roo.each(this.barItems, function(i){
7217 i.el.setStyle('width', width + '%');
7218 i.topEl.el.setStyle('width', width + '%');
7219 i.bottomEl.el.setStyle('width', width + '%');
7233 * @class Roo.bootstrap.nav.ProgressBarItem
7234 * @extends Roo.bootstrap.Component
7235 * Bootstrap NavProgressBarItem class
7236 * @cfg {String} rid the reference id
7237 * @cfg {Boolean} active (true|false) Is item active default false
7238 * @cfg {Boolean} disabled (true|false) Is item active default false
7239 * @cfg {String} html
7240 * @cfg {String} position (top|bottom) text position default bottom
7241 * @cfg {String} icon show icon instead of number
7244 * Create a new NavProgressBarItem
7245 * @param {Object} config The config object
7247 Roo.bootstrap.nav.ProgressBarItem = function(config){
7248 Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7253 * The raw click event for the entire grid.
7254 * @param {Roo.bootstrap.nav.ProgressBarItem} this
7255 * @param {Roo.EventObject} e
7262 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component, {
7268 position : 'bottom',
7271 getAutoCreate : function()
7273 var iconCls = 'roo-navigation-bar-item-icon';
7275 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7279 cls: 'roo-navigation-bar-item',
7289 cfg.cls += ' active';
7292 cfg.cls += ' disabled';
7298 disable : function()
7300 this.setDisabled(true);
7305 this.setDisabled(false);
7308 initEvents: function()
7310 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7312 this.iconEl.on('click', this.onClick, this);
7315 onClick : function(e)
7323 if(this.fireEvent('click', this, e) === false){
7327 this.parent().setActiveItem(this);
7330 isActive: function ()
7335 setActive : function(state)
7337 if(this.active == state){
7341 this.active = state;
7344 this.el.addClass('active');
7348 this.el.removeClass('active');
7353 setDisabled : function(state)
7355 if(this.disabled == state){
7359 this.disabled = state;
7362 this.el.addClass('disabled');
7366 this.el.removeClass('disabled');
7369 tooltipEl : function()
7371 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7382 Roo.namespace('Roo.bootstrap.breadcrumb');
7386 * @class Roo.bootstrap.breadcrumb.Nav
7387 * @extends Roo.bootstrap.Component
7388 * Bootstrap Breadcrumb Nav Class
7390 * @children Roo.bootstrap.breadcrumb.Item
7393 * Create a new breadcrumb.Nav
7394 * @param {Object} config The config object
7398 Roo.bootstrap.breadcrumb.Nav = function(config){
7399 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7404 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
7406 getAutoCreate : function()
7423 initEvents: function()
7425 this.olEl = this.el.select('ol',true).first();
7427 getChildContainer : function()
7443 * @class Roo.bootstrap.breadcrumb.Nav
7444 * @extends Roo.bootstrap.Component
7445 * @children Roo.bootstrap.Component
7446 * @parent Roo.bootstrap.breadcrumb.Nav
7447 * Bootstrap Breadcrumb Nav Class
7450 * @cfg {String} html the content of the link.
7451 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7452 * @cfg {Boolean} active is it active
7456 * Create a new breadcrumb.Nav
7457 * @param {Object} config The config object
7460 Roo.bootstrap.breadcrumb.Item = function(config){
7461 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7466 * The img click event for the img.
7467 * @param {Roo.EventObject} e
7474 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7479 getAutoCreate : function()
7484 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7486 if (this.href !== false) {
7493 cfg.html = this.html;
7499 initEvents: function()
7502 this.el.select('a', true).first().on('click',this.onClick, this)
7506 onClick : function(e)
7509 this.fireEvent('click',this, e);
7522 * @class Roo.bootstrap.Row
7523 * @extends Roo.bootstrap.Component
7524 * @children Roo.bootstrap.Component
7525 * Bootstrap Row class (contains columns...)
7529 * @param {Object} config The config object
7532 Roo.bootstrap.Row = function(config){
7533 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7536 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7538 getAutoCreate : function(){
7557 * @class Roo.bootstrap.Pagination
7558 * @extends Roo.bootstrap.Component
7559 * @children Roo.bootstrap.Pagination
7560 * Bootstrap Pagination class
7562 * @cfg {String} size (xs|sm|md|lg|xl)
7563 * @cfg {Boolean} inverse
7566 * Create a new Pagination
7567 * @param {Object} config The config object
7570 Roo.bootstrap.Pagination = function(config){
7571 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7574 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7580 getAutoCreate : function(){
7586 cfg.cls += ' inverse';
7592 cfg.cls += " " + this.cls;
7610 * @class Roo.bootstrap.PaginationItem
7611 * @extends Roo.bootstrap.Component
7612 * Bootstrap PaginationItem class
7613 * @cfg {String} html text
7614 * @cfg {String} href the link
7615 * @cfg {Boolean} preventDefault (true | false) default true
7616 * @cfg {Boolean} active (true | false) default false
7617 * @cfg {Boolean} disabled default false
7621 * Create a new PaginationItem
7622 * @param {Object} config The config object
7626 Roo.bootstrap.PaginationItem = function(config){
7627 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7632 * The raw click event for the entire grid.
7633 * @param {Roo.EventObject} e
7639 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7643 preventDefault: true,
7648 getAutoCreate : function(){
7654 href : this.href ? this.href : '#',
7655 html : this.html ? this.html : ''
7665 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7669 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7675 initEvents: function() {
7677 this.el.on('click', this.onClick, this);
7680 onClick : function(e)
7682 Roo.log('PaginationItem on click ');
7683 if(this.preventDefault){
7691 this.fireEvent('click', this, e);
7707 * @class Roo.bootstrap.Slider
7708 * @extends Roo.bootstrap.Component
7709 * Bootstrap Slider class
7712 * Create a new Slider
7713 * @param {Object} config The config object
7716 Roo.bootstrap.Slider = function(config){
7717 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7720 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7722 getAutoCreate : function(){
7726 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7730 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7742 * Ext JS Library 1.1.1
7743 * Copyright(c) 2006-2007, Ext JS, LLC.
7745 * Originally Released Under LGPL - original licence link has changed is not relivant.
7748 * <script type="text/javascript">
7751 * @extends Roo.dd.DDProxy
7752 * @class Roo.grid.SplitDragZone
7753 * Support for Column Header resizing
7755 * @param {Object} config
7758 // This is a support class used internally by the Grid components
7759 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7761 this.view = grid.getView();
7762 this.proxy = this.view.resizeProxy;
7763 Roo.grid.SplitDragZone.superclass.constructor.call(
7766 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7768 dragElId : Roo.id(this.proxy.dom),
7773 this.setHandleElId(Roo.id(hd));
7774 if (hd2 !== false) {
7775 this.setOuterHandleElId(Roo.id(hd2));
7778 this.scroll = false;
7780 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7781 fly: Roo.Element.fly,
7783 b4StartDrag : function(x, y){
7784 this.view.headersDisabled = true;
7785 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7786 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7788 this.proxy.setHeight(h);
7790 // for old system colWidth really stored the actual width?
7791 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7792 // which in reality did not work.. - it worked only for fixed sizes
7793 // for resizable we need to use actual sizes.
7794 var w = this.cm.getColumnWidth(this.cellIndex);
7795 if (!this.view.mainWrap) {
7797 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7802 // this was w-this.grid.minColumnWidth;
7803 // doesnt really make sense? - w = thie curren width or the rendered one?
7804 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7805 this.resetConstraints();
7806 this.setXConstraint(minw, 1000);
7807 this.setYConstraint(0, 0);
7808 this.minX = x - minw;
7809 this.maxX = x + 1000;
7811 if (!this.view.mainWrap) { // this is Bootstrap code..
7812 this.getDragEl().style.display='block';
7815 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7819 handleMouseDown : function(e){
7820 ev = Roo.EventObject.setEvent(e);
7821 var t = this.fly(ev.getTarget());
7822 if(t.hasClass("x-grid-split")){
7823 this.cellIndex = this.view.getCellIndex(t.dom);
7825 this.cm = this.grid.colModel;
7826 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7827 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7832 endDrag : function(e){
7833 this.view.headersDisabled = false;
7834 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7835 var diff = endX - this.startPos;
7837 var w = this.cm.getColumnWidth(this.cellIndex);
7838 if (!this.view.mainWrap) {
7841 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7844 autoOffset : function(){
7849 * Ext JS Library 1.1.1
7850 * Copyright(c) 2006-2007, Ext JS, LLC.
7852 * Originally Released Under LGPL - original licence link has changed is not relivant.
7855 * <script type="text/javascript">
7859 * @class Roo.grid.AbstractSelectionModel
7860 * @extends Roo.util.Observable
7862 * Abstract base class for grid SelectionModels. It provides the interface that should be
7863 * implemented by descendant classes. This class should not be directly instantiated.
7866 Roo.grid.AbstractSelectionModel = function(){
7867 this.locked = false;
7868 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7871 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7872 /** @ignore Called by the grid automatically. Do not call directly. */
7873 init : function(grid){
7879 * Locks the selections.
7886 * Unlocks the selections.
7888 unlock : function(){
7889 this.locked = false;
7893 * Returns true if the selections are locked.
7896 isLocked : function(){
7901 * Ext JS Library 1.1.1
7902 * Copyright(c) 2006-2007, Ext JS, LLC.
7904 * Originally Released Under LGPL - original licence link has changed is not relivant.
7907 * <script type="text/javascript">
7910 * @extends Roo.grid.AbstractSelectionModel
7911 * @class Roo.grid.RowSelectionModel
7912 * The default SelectionModel used by {@link Roo.grid.Grid}.
7913 * It supports multiple selections and keyboard selection/navigation.
7915 * @param {Object} config
7917 Roo.grid.RowSelectionModel = function(config){
7918 Roo.apply(this, config);
7919 this.selections = new Roo.util.MixedCollection(false, function(o){
7924 this.lastActive = false;
7928 * @event selectionchange
7929 * Fires when the selection changes
7930 * @param {SelectionModel} this
7932 "selectionchange" : true,
7934 * @event afterselectionchange
7935 * Fires after the selection changes (eg. by key press or clicking)
7936 * @param {SelectionModel} this
7938 "afterselectionchange" : true,
7940 * @event beforerowselect
7941 * Fires when a row is selected being selected, return false to cancel.
7942 * @param {SelectionModel} this
7943 * @param {Number} rowIndex The selected index
7944 * @param {Boolean} keepExisting False if other selections will be cleared
7946 "beforerowselect" : true,
7949 * Fires when a row is selected.
7950 * @param {SelectionModel} this
7951 * @param {Number} rowIndex The selected index
7952 * @param {Roo.data.Record} r The record
7956 * @event rowdeselect
7957 * Fires when a row is deselected.
7958 * @param {SelectionModel} this
7959 * @param {Number} rowIndex The selected index
7961 "rowdeselect" : true
7963 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7964 this.locked = false;
7967 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7969 * @cfg {Boolean} singleSelect
7970 * True to allow selection of only one row at a time (defaults to false)
7972 singleSelect : false,
7975 initEvents : function(){
7977 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7978 this.grid.on("mousedown", this.handleMouseDown, this);
7979 }else{ // allow click to work like normal
7980 this.grid.on("rowclick", this.handleDragableRowClick, this);
7982 // bootstrap does not have a view..
7983 var view = this.grid.view ? this.grid.view : this.grid;
7984 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7987 this.selectPrevious(e.shiftKey);
7988 }else if(this.last !== false && this.lastActive !== false){
7989 var last = this.last;
7990 this.selectRange(this.last, this.lastActive-1);
7991 view.focusRow(this.lastActive);
7996 this.selectFirstRow();
7998 this.fireEvent("afterselectionchange", this);
8000 "down" : function(e){
8002 this.selectNext(e.shiftKey);
8003 }else if(this.last !== false && this.lastActive !== false){
8004 var last = this.last;
8005 this.selectRange(this.last, this.lastActive+1);
8006 view.focusRow(this.lastActive);
8011 this.selectFirstRow();
8013 this.fireEvent("afterselectionchange", this);
8019 view.on("refresh", this.onRefresh, this);
8020 view.on("rowupdated", this.onRowUpdated, this);
8021 view.on("rowremoved", this.onRemove, this);
8025 onRefresh : function(){
8026 var ds = this.grid.ds, i, v = this.grid.view;
8027 var s = this.selections;
8029 if((i = ds.indexOfId(r.id)) != -1){
8031 s.add(ds.getAt(i)); // updating the selection relate data
8039 onRemove : function(v, index, r){
8040 this.selections.remove(r);
8044 onRowUpdated : function(v, index, r){
8045 if(this.isSelected(r)){
8046 v.onRowSelect(index);
8052 * @param {Array} records The records to select
8053 * @param {Boolean} keepExisting (optional) True to keep existing selections
8055 selectRecords : function(records, keepExisting){
8057 this.clearSelections();
8059 var ds = this.grid.ds;
8060 for(var i = 0, len = records.length; i < len; i++){
8061 this.selectRow(ds.indexOf(records[i]), true);
8066 * Gets the number of selected rows.
8069 getCount : function(){
8070 return this.selections.length;
8074 * Selects the first row in the grid.
8076 selectFirstRow : function(){
8081 * Select the last row.
8082 * @param {Boolean} keepExisting (optional) True to keep existing selections
8084 selectLastRow : function(keepExisting){
8085 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8089 * Selects the row immediately following the last selected row.
8090 * @param {Boolean} keepExisting (optional) True to keep existing selections
8092 selectNext : function(keepExisting){
8093 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8094 this.selectRow(this.last+1, keepExisting);
8095 var view = this.grid.view ? this.grid.view : this.grid;
8096 view.focusRow(this.last);
8101 * Selects the row that precedes the last selected row.
8102 * @param {Boolean} keepExisting (optional) True to keep existing selections
8104 selectPrevious : function(keepExisting){
8106 this.selectRow(this.last-1, keepExisting);
8107 var view = this.grid.view ? this.grid.view : this.grid;
8108 view.focusRow(this.last);
8113 * Returns the selected records
8114 * @return {Array} Array of selected records
8116 getSelections : function(){
8117 return [].concat(this.selections.items);
8121 * Returns the first selected record.
8124 getSelected : function(){
8125 return this.selections.itemAt(0);
8130 * Clears all selections.
8132 clearSelections : function(fast){
8137 var ds = this.grid.ds;
8138 var s = this.selections;
8140 this.deselectRow(ds.indexOfId(r.id));
8144 this.selections.clear();
8153 selectAll : function(){
8157 this.selections.clear();
8158 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8159 this.selectRow(i, true);
8164 * Returns True if there is a selection.
8167 hasSelection : function(){
8168 return this.selections.length > 0;
8172 * Returns True if the specified row is selected.
8173 * @param {Number/Record} record The record or index of the record to check
8176 isSelected : function(index){
8177 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8178 return (r && this.selections.key(r.id) ? true : false);
8182 * Returns True if the specified record id is selected.
8183 * @param {String} id The id of record to check
8186 isIdSelected : function(id){
8187 return (this.selections.key(id) ? true : false);
8191 handleMouseDown : function(e, t)
8193 var view = this.grid.view ? this.grid.view : this.grid;
8195 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8198 if(e.shiftKey && this.last !== false){
8199 var last = this.last;
8200 this.selectRange(last, rowIndex, e.ctrlKey);
8201 this.last = last; // reset the last
8202 view.focusRow(rowIndex);
8204 var isSelected = this.isSelected(rowIndex);
8205 if(e.button !== 0 && isSelected){
8206 view.focusRow(rowIndex);
8207 }else if(e.ctrlKey && isSelected){
8208 this.deselectRow(rowIndex);
8209 }else if(!isSelected){
8210 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8211 view.focusRow(rowIndex);
8214 this.fireEvent("afterselectionchange", this);
8217 handleDragableRowClick : function(grid, rowIndex, e)
8219 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8220 this.selectRow(rowIndex, false);
8221 var view = this.grid.view ? this.grid.view : this.grid;
8222 view.focusRow(rowIndex);
8223 this.fireEvent("afterselectionchange", this);
8228 * Selects multiple rows.
8229 * @param {Array} rows Array of the indexes of the row to select
8230 * @param {Boolean} keepExisting (optional) True to keep existing selections
8232 selectRows : function(rows, keepExisting){
8234 this.clearSelections();
8236 for(var i = 0, len = rows.length; i < len; i++){
8237 this.selectRow(rows[i], true);
8242 * Selects a range of rows. All rows in between startRow and endRow are also selected.
8243 * @param {Number} startRow The index of the first row in the range
8244 * @param {Number} endRow The index of the last row in the range
8245 * @param {Boolean} keepExisting (optional) True to retain existing selections
8247 selectRange : function(startRow, endRow, keepExisting){
8252 this.clearSelections();
8254 if(startRow <= endRow){
8255 for(var i = startRow; i <= endRow; i++){
8256 this.selectRow(i, true);
8259 for(var i = startRow; i >= endRow; i--){
8260 this.selectRow(i, true);
8266 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8267 * @param {Number} startRow The index of the first row in the range
8268 * @param {Number} endRow The index of the last row in the range
8270 deselectRange : function(startRow, endRow, preventViewNotify){
8274 for(var i = startRow; i <= endRow; i++){
8275 this.deselectRow(i, preventViewNotify);
8281 * @param {Number} row The index of the row to select
8282 * @param {Boolean} keepExisting (optional) True to keep existing selections
8284 selectRow : function(index, keepExisting, preventViewNotify){
8285 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8288 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8289 if(!keepExisting || this.singleSelect){
8290 this.clearSelections();
8292 var r = this.grid.ds.getAt(index);
8293 this.selections.add(r);
8294 this.last = this.lastActive = index;
8295 if(!preventViewNotify){
8296 var view = this.grid.view ? this.grid.view : this.grid;
8297 view.onRowSelect(index);
8299 this.fireEvent("rowselect", this, index, r);
8300 this.fireEvent("selectionchange", this);
8306 * @param {Number} row The index of the row to deselect
8308 deselectRow : function(index, preventViewNotify){
8312 if(this.last == index){
8315 if(this.lastActive == index){
8316 this.lastActive = false;
8318 var r = this.grid.ds.getAt(index);
8319 this.selections.remove(r);
8320 if(!preventViewNotify){
8321 var view = this.grid.view ? this.grid.view : this.grid;
8322 view.onRowDeselect(index);
8324 this.fireEvent("rowdeselect", this, index);
8325 this.fireEvent("selectionchange", this);
8329 restoreLast : function(){
8331 this.last = this._last;
8336 acceptsNav : function(row, col, cm){
8337 return !cm.isHidden(col) && cm.isCellEditable(col, row);
8341 onEditorKey : function(field, e){
8342 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8347 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8349 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8351 }else if(k == e.ENTER && !e.ctrlKey){
8355 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8357 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8359 }else if(k == e.ESC){
8363 g.startEditing(newCell[0], newCell[1]);
8368 * Ext JS Library 1.1.1
8369 * Copyright(c) 2006-2007, Ext JS, LLC.
8371 * Originally Released Under LGPL - original licence link has changed is not relivant.
8374 * <script type="text/javascript">
8379 * @class Roo.grid.ColumnModel
8380 * @extends Roo.util.Observable
8381 * This is the default implementation of a ColumnModel used by the Grid. It defines
8382 * the columns in the grid.
8385 var colModel = new Roo.grid.ColumnModel([
8386 {header: "Ticker", width: 60, sortable: true, locked: true},
8387 {header: "Company Name", width: 150, sortable: true},
8388 {header: "Market Cap.", width: 100, sortable: true},
8389 {header: "$ Sales", width: 100, sortable: true, renderer: money},
8390 {header: "Employees", width: 100, sortable: true, resizable: false}
8395 * The config options listed for this class are options which may appear in each
8396 * individual column definition.
8397 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8399 * @param {Object} config An Array of column config objects. See this class's
8400 * config objects for details.
8402 Roo.grid.ColumnModel = function(config){
8404 * The config passed into the constructor
8406 this.config = []; //config;
8409 // if no id, create one
8410 // if the column does not have a dataIndex mapping,
8411 // map it to the order it is in the config
8412 for(var i = 0, len = config.length; i < len; i++){
8413 this.addColumn(config[i]);
8418 * The width of columns which have no width specified (defaults to 100)
8421 this.defaultWidth = 100;
8424 * Default sortable of columns which have no sortable specified (defaults to false)
8427 this.defaultSortable = false;
8431 * @event widthchange
8432 * Fires when the width of a column changes.
8433 * @param {ColumnModel} this
8434 * @param {Number} columnIndex The column index
8435 * @param {Number} newWidth The new width
8437 "widthchange": true,
8439 * @event headerchange
8440 * Fires when the text of a header changes.
8441 * @param {ColumnModel} this
8442 * @param {Number} columnIndex The column index
8443 * @param {Number} newText The new header text
8445 "headerchange": true,
8447 * @event hiddenchange
8448 * Fires when a column is hidden or "unhidden".
8449 * @param {ColumnModel} this
8450 * @param {Number} columnIndex The column index
8451 * @param {Boolean} hidden true if hidden, false otherwise
8453 "hiddenchange": true,
8455 * @event columnmoved
8456 * Fires when a column is moved.
8457 * @param {ColumnModel} this
8458 * @param {Number} oldIndex
8459 * @param {Number} newIndex
8461 "columnmoved" : true,
8463 * @event columlockchange
8464 * Fires when a column's locked state is changed
8465 * @param {ColumnModel} this
8466 * @param {Number} colIndex
8467 * @param {Boolean} locked true if locked
8469 "columnlockchange" : true
8471 Roo.grid.ColumnModel.superclass.constructor.call(this);
8473 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8475 * @cfg {String} header [required] The header text to display in the Grid view.
8478 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8481 * @cfg {String} smHeader Header at Bootsrap Small width
8484 * @cfg {String} mdHeader Header at Bootsrap Medium width
8487 * @cfg {String} lgHeader Header at Bootsrap Large width
8490 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8493 * @cfg {String} dataIndex The name of the field in the grid's {@link Roo.data.Store}'s
8494 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8495 * specified, the column's index is used as an index into the Record's data Array.
8498 * @cfg {Number} width The initial width in pixels of the column. Using this
8499 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8502 * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8503 * Defaults to the value of the {@link #defaultSortable} property.
8504 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8507 * @cfg {Boolean} locked True to lock the column in place while scrolling the Grid. Defaults to false.
8510 * @cfg {Boolean} fixed True if the column width cannot be changed. Defaults to false.
8513 * @cfg {Boolean} resizable False to disable column resizing. Defaults to true.
8516 * @cfg {Boolean} hidden True to hide the column. Defaults to false.
8519 * @cfg {Function} renderer A function used to generate HTML markup for a cell
8520 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8521 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8522 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8525 * @cfg {Roo.grid.GridEditor} editor For grid editors - returns the grid editor
8528 * @cfg {String} align (left|right) Set the CSS text-align property of the column. Defaults to undefined (left).
8531 * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined (middle)
8534 * @cfg {String} cursor ( auto|default|none|context-menu|help|pointer|progress|wait|cell|crosshair|text|vertical-text|alias|copy|move|no-drop|not-allowed|e-resize|n-resize|ne-resize|nw-resize|s-resize|se-resize|sw-resize|w-resize|ew-resize|ns-resize|nesw-resize|nwse-resize|col-resize|row-resize|all-scroll|zoom-in|zoom-out|grab|grabbing)
8537 * @cfg {String} tooltip mouse over tooltip text
8540 * @cfg {Number} xs can be '0' for hidden at this size (number less than 12)
8543 * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8546 * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8549 * @cfg {Number} lg can be '0' for hidden at this size (number less than 12)
8552 * @cfg {Number} xl can be '0' for hidden at this size (number less than 12)
8555 * Returns the id of the column at the specified index.
8556 * @param {Number} index The column index
8557 * @return {String} the id
8559 getColumnId : function(index){
8560 return this.config[index].id;
8564 * Returns the column for a specified id.
8565 * @param {String} id The column id
8566 * @return {Object} the column
8568 getColumnById : function(id){
8569 return this.lookup[id];
8574 * Returns the column Object for a specified dataIndex.
8575 * @param {String} dataIndex The column dataIndex
8576 * @return {Object|Boolean} the column or false if not found
8578 getColumnByDataIndex: function(dataIndex){
8579 var index = this.findColumnIndex(dataIndex);
8580 return index > -1 ? this.config[index] : false;
8584 * Returns the index for a specified column id.
8585 * @param {String} id The column id
8586 * @return {Number} the index, or -1 if not found
8588 getIndexById : function(id){
8589 for(var i = 0, len = this.config.length; i < len; i++){
8590 if(this.config[i].id == id){
8598 * Returns the index for a specified column dataIndex.
8599 * @param {String} dataIndex The column dataIndex
8600 * @return {Number} the index, or -1 if not found
8603 findColumnIndex : function(dataIndex){
8604 for(var i = 0, len = this.config.length; i < len; i++){
8605 if(this.config[i].dataIndex == dataIndex){
8613 moveColumn : function(oldIndex, newIndex){
8614 var c = this.config[oldIndex];
8615 this.config.splice(oldIndex, 1);
8616 this.config.splice(newIndex, 0, c);
8617 this.dataMap = null;
8618 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8621 isLocked : function(colIndex){
8622 return this.config[colIndex].locked === true;
8625 setLocked : function(colIndex, value, suppressEvent){
8626 if(this.isLocked(colIndex) == value){
8629 this.config[colIndex].locked = value;
8631 this.fireEvent("columnlockchange", this, colIndex, value);
8635 getTotalLockedWidth : function(){
8637 for(var i = 0; i < this.config.length; i++){
8638 if(this.isLocked(i) && !this.isHidden(i)){
8639 this.totalWidth += this.getColumnWidth(i);
8645 getLockedCount : function(){
8646 for(var i = 0, len = this.config.length; i < len; i++){
8647 if(!this.isLocked(i)){
8652 return this.config.length;
8656 * Returns the number of columns.
8659 getColumnCount : function(visibleOnly){
8660 if(visibleOnly === true){
8662 for(var i = 0, len = this.config.length; i < len; i++){
8663 if(!this.isHidden(i)){
8669 return this.config.length;
8673 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8674 * @param {Function} fn
8675 * @param {Object} scope (optional)
8676 * @return {Array} result
8678 getColumnsBy : function(fn, scope){
8680 for(var i = 0, len = this.config.length; i < len; i++){
8681 var c = this.config[i];
8682 if(fn.call(scope||this, c, i) === true){
8690 * Returns true if the specified column is sortable.
8691 * @param {Number} col The column index
8694 isSortable : function(col){
8695 if(typeof this.config[col].sortable == "undefined"){
8696 return this.defaultSortable;
8698 return this.config[col].sortable;
8702 * Returns the rendering (formatting) function defined for the column.
8703 * @param {Number} col The column index.
8704 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8706 getRenderer : function(col){
8707 if(!this.config[col].renderer){
8708 return Roo.grid.ColumnModel.defaultRenderer;
8710 return this.config[col].renderer;
8714 * Sets the rendering (formatting) function for a column.
8715 * @param {Number} col The column index
8716 * @param {Function} fn The function to use to process the cell's raw data
8717 * to return HTML markup for the grid view. The render function is called with
8718 * the following parameters:<ul>
8719 * <li>Data value.</li>
8720 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8721 * <li>css A CSS style string to apply to the table cell.</li>
8722 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8723 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8724 * <li>Row index</li>
8725 * <li>Column index</li>
8726 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8728 setRenderer : function(col, fn){
8729 this.config[col].renderer = fn;
8733 * Returns the width for the specified column.
8734 * @param {Number} col The column index
8735 * @param (optional) {String} gridSize bootstrap width size.
8738 getColumnWidth : function(col, gridSize)
8740 var cfg = this.config[col];
8742 if (typeof(gridSize) == 'undefined') {
8743 return cfg.width * 1 || this.defaultWidth;
8745 if (gridSize === false) { // if we set it..
8746 return cfg.width || false;
8748 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8750 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8751 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8754 return cfg[ sizes[i] ];
8761 * Sets the width for a column.
8762 * @param {Number} col The column index
8763 * @param {Number} width The new width
8765 setColumnWidth : function(col, width, suppressEvent){
8766 this.config[col].width = width;
8767 this.totalWidth = null;
8769 this.fireEvent("widthchange", this, col, width);
8774 * Returns the total width of all columns.
8775 * @param {Boolean} includeHidden True to include hidden column widths
8778 getTotalWidth : function(includeHidden){
8779 if(!this.totalWidth){
8780 this.totalWidth = 0;
8781 for(var i = 0, len = this.config.length; i < len; i++){
8782 if(includeHidden || !this.isHidden(i)){
8783 this.totalWidth += this.getColumnWidth(i);
8787 return this.totalWidth;
8791 * Returns the header for the specified column.
8792 * @param {Number} col The column index
8795 getColumnHeader : function(col){
8796 return this.config[col].header;
8800 * Sets the header for a column.
8801 * @param {Number} col The column index
8802 * @param {String} header The new header
8804 setColumnHeader : function(col, header){
8805 this.config[col].header = header;
8806 this.fireEvent("headerchange", this, col, header);
8810 * Returns the tooltip for the specified column.
8811 * @param {Number} col The column index
8814 getColumnTooltip : function(col){
8815 return this.config[col].tooltip;
8818 * Sets the tooltip for a column.
8819 * @param {Number} col The column index
8820 * @param {String} tooltip The new tooltip
8822 setColumnTooltip : function(col, tooltip){
8823 this.config[col].tooltip = tooltip;
8827 * Returns the dataIndex for the specified column.
8828 * @param {Number} col The column index
8831 getDataIndex : function(col){
8832 return this.config[col].dataIndex;
8836 * Sets the dataIndex for a column.
8837 * @param {Number} col The column index
8838 * @param {Number} dataIndex The new dataIndex
8840 setDataIndex : function(col, dataIndex){
8841 this.config[col].dataIndex = dataIndex;
8847 * Returns true if the cell is editable.
8848 * @param {Number} colIndex The column index
8849 * @param {Number} rowIndex The row index - this is nto actually used..?
8852 isCellEditable : function(colIndex, rowIndex){
8853 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8857 * Returns the editor defined for the cell/column.
8858 * return false or null to disable editing.
8859 * @param {Number} colIndex The column index
8860 * @param {Number} rowIndex The row index
8863 getCellEditor : function(colIndex, rowIndex){
8864 return this.config[colIndex].editor;
8868 * Sets if a column is editable.
8869 * @param {Number} col The column index
8870 * @param {Boolean} editable True if the column is editable
8872 setEditable : function(col, editable){
8873 this.config[col].editable = editable;
8878 * Returns true if the column is hidden.
8879 * @param {Number} colIndex The column index
8882 isHidden : function(colIndex){
8883 return this.config[colIndex].hidden;
8888 * Returns true if the column width cannot be changed
8890 isFixed : function(colIndex){
8891 return this.config[colIndex].fixed;
8895 * Returns true if the column can be resized
8898 isResizable : function(colIndex){
8899 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8902 * Sets if a column is hidden.
8903 * @param {Number} colIndex The column index
8904 * @param {Boolean} hidden True if the column is hidden
8906 setHidden : function(colIndex, hidden){
8907 this.config[colIndex].hidden = hidden;
8908 this.totalWidth = null;
8909 this.fireEvent("hiddenchange", this, colIndex, hidden);
8913 * Sets the editor for a column.
8914 * @param {Number} col The column index
8915 * @param {Object} editor The editor object
8917 setEditor : function(col, editor){
8918 this.config[col].editor = editor;
8921 * Add a column (experimental...) - defaults to adding to the end..
8922 * @param {Object} config
8924 addColumn : function(c)
8927 var i = this.config.length;
8930 if(typeof c.dataIndex == "undefined"){
8933 if(typeof c.renderer == "string"){
8934 c.renderer = Roo.util.Format[c.renderer];
8936 if(typeof c.id == "undefined"){
8939 if(c.editor && c.editor.xtype){
8940 c.editor = Roo.factory(c.editor, Roo.grid);
8942 if(c.editor && c.editor.isFormField){
8943 c.editor = new Roo.grid.GridEditor(c.editor);
8945 this.lookup[c.id] = c;
8950 Roo.grid.ColumnModel.defaultRenderer = function(value)
8952 if(typeof value == "object") {
8955 if(typeof value == "string" && value.length < 1){
8959 return String.format("{0}", value);
8962 // Alias for backwards compatibility
8963 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8966 * Ext JS Library 1.1.1
8967 * Copyright(c) 2006-2007, Ext JS, LLC.
8969 * Originally Released Under LGPL - original licence link has changed is not relivant.
8972 * <script type="text/javascript">
8976 * @class Roo.LoadMask
8977 * A simple utility class for generically masking elements while loading data. If the element being masked has
8978 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8979 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8980 * element's UpdateManager load indicator and will be destroyed after the initial load.
8982 * Create a new LoadMask
8983 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8984 * @param {Object} config The config object
8986 Roo.LoadMask = function(el, config){
8987 this.el = Roo.get(el);
8988 Roo.apply(this, config);
8990 this.store.on('beforeload', this.onBeforeLoad, this);
8991 this.store.on('load', this.onLoad, this);
8992 this.store.on('loadexception', this.onLoadException, this);
8993 this.removeMask = false;
8995 var um = this.el.getUpdateManager();
8996 um.showLoadIndicator = false; // disable the default indicator
8997 um.on('beforeupdate', this.onBeforeLoad, this);
8998 um.on('update', this.onLoad, this);
8999 um.on('failure', this.onLoad, this);
9000 this.removeMask = true;
9004 Roo.LoadMask.prototype = {
9006 * @cfg {Boolean} removeMask
9007 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
9008 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
9013 * The text to display in a centered loading message box (defaults to 'Loading...')
9017 * @cfg {String} msgCls
9018 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9020 msgCls : 'x-mask-loading',
9023 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9029 * Disables the mask to prevent it from being displayed
9031 disable : function(){
9032 this.disabled = true;
9036 * Enables the mask so that it can be displayed
9038 enable : function(){
9039 this.disabled = false;
9042 onLoadException : function()
9046 if (typeof(arguments[3]) != 'undefined') {
9047 Roo.MessageBox.alert("Error loading",arguments[3]);
9051 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9052 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9059 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9064 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9068 onBeforeLoad : function(){
9070 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9075 destroy : function(){
9077 this.store.un('beforeload', this.onBeforeLoad, this);
9078 this.store.un('load', this.onLoad, this);
9079 this.store.un('loadexception', this.onLoadException, this);
9081 var um = this.el.getUpdateManager();
9082 um.un('beforeupdate', this.onBeforeLoad, this);
9083 um.un('update', this.onLoad, this);
9084 um.un('failure', this.onLoad, this);
9088 * @class Roo.bootstrap.Table
9090 * @extends Roo.bootstrap.Component
9091 * @children Roo.bootstrap.TableBody
9092 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
9093 * Similar to Roo.grid.Grid
9095 var table = Roo.factory({
9097 xns : Roo.bootstrap,
9098 autoSizeColumns: true,
9105 sortInfo : { direction : 'ASC', field: 'name' },
9107 xtype : 'HttpProxy',
9110 url : 'https://example.com/some.data.url.json'
9113 xtype : 'JsonReader',
9115 fields : [ 'id', 'name', whatever' ],
9122 xtype : 'ColumnModel',
9126 dataIndex : 'is_in_group',
9129 renderer : function(v, x , r) {
9131 return String.format("{0}", v)
9137 xtype : 'RowSelectionModel',
9138 xns : Roo.bootstrap.Table
9139 // you can add listeners to catch selection change here....
9145 grid.render(Roo.get("some-div"));
9148 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
9153 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9154 * @cfg {Roo.data.Store} store The data store to use
9155 * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9157 * @cfg {String} cls table class
9160 * @cfg {string} empty_results Text to display for no results
9161 * @cfg {boolean} striped Should the rows be alternative striped
9162 * @cfg {boolean} bordered Add borders to the table
9163 * @cfg {boolean} hover Add hover highlighting
9164 * @cfg {boolean} condensed Format condensed
9165 * @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,
9166 * also adds table-responsive (see bootstrap docs for details)
9167 * @cfg {Boolean} loadMask (true|false) default false
9168 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9169 * @cfg {Boolean} footerRow (true|false) generate tfoot with columns of values, default false
9170 * @cfg {Boolean} headerShow (true|false) generate thead, default true
9171 * @cfg {Boolean} rowSelection (true|false) default false
9172 * @cfg {Boolean} cellSelection (true|false) default false
9173 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9174 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
9175 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
9176 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
9177 * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9178 * @cfg {Boolean} disableAutoSize disable autoSize() and initCSS()
9181 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
9184 * Create a new Table
9185 * @param {Object} config The config object
9188 Roo.bootstrap.Table = function(config)
9190 Roo.bootstrap.Table.superclass.constructor.call(this, config);
9193 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9194 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9195 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9196 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9198 this.view = this; // compat with grid.
9200 this.sm = this.sm || {xtype: 'RowSelectionModel'};
9202 this.sm.grid = this;
9203 this.selModel = Roo.factory(this.sm, Roo.grid);
9204 this.sm = this.selModel;
9205 this.sm.xmodule = this.xmodule || false;
9208 if (this.cm && typeof(this.cm.config) == 'undefined') {
9209 this.colModel = new Roo.grid.ColumnModel(this.cm);
9210 this.cm = this.colModel;
9211 this.cm.xmodule = this.xmodule || false;
9214 this.store= Roo.factory(this.store, Roo.data);
9215 this.ds = this.store;
9216 this.ds.xmodule = this.xmodule || false;
9219 if (this.footer && this.store) {
9220 this.footer.dataSource = this.ds;
9221 this.footer = Roo.factory(this.footer);
9228 * Fires when a cell is clicked
9229 * @param {Roo.bootstrap.Table} this
9230 * @param {Roo.Element} el
9231 * @param {Number} rowIndex
9232 * @param {Number} columnIndex
9233 * @param {Roo.EventObject} e
9237 * @event celldblclick
9238 * Fires when a cell is double clicked
9239 * @param {Roo.bootstrap.Table} this
9240 * @param {Roo.Element} el
9241 * @param {Number} rowIndex
9242 * @param {Number} columnIndex
9243 * @param {Roo.EventObject} e
9245 "celldblclick" : true,
9248 * Fires when a row is clicked
9249 * @param {Roo.bootstrap.Table} this
9250 * @param {Roo.Element} el
9251 * @param {Number} rowIndex
9252 * @param {Roo.EventObject} e
9256 * @event rowdblclick
9257 * Fires when a row is double clicked
9258 * @param {Roo.bootstrap.Table} this
9259 * @param {Roo.Element} el
9260 * @param {Number} rowIndex
9261 * @param {Roo.EventObject} e
9263 "rowdblclick" : true,
9266 * Fires when a mouseover occur
9267 * @param {Roo.bootstrap.Table} this
9268 * @param {Roo.Element} el
9269 * @param {Number} rowIndex
9270 * @param {Number} columnIndex
9271 * @param {Roo.EventObject} e
9276 * Fires when a mouseout occur
9277 * @param {Roo.bootstrap.Table} this
9278 * @param {Roo.Element} el
9279 * @param {Number} rowIndex
9280 * @param {Number} columnIndex
9281 * @param {Roo.EventObject} e
9286 * Fires when a row is rendered, so you can change add a style to it.
9287 * @param {Roo.bootstrap.Table} this
9288 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
9292 * @event rowsrendered
9293 * Fires when all the rows have been rendered
9294 * @param {Roo.bootstrap.Table} this
9296 'rowsrendered' : true,
9298 * @event contextmenu
9299 * The raw contextmenu event for the entire grid.
9300 * @param {Roo.EventObject} e
9302 "contextmenu" : true,
9304 * @event rowcontextmenu
9305 * Fires when a row is right clicked
9306 * @param {Roo.bootstrap.Table} this
9307 * @param {Number} rowIndex
9308 * @param {Roo.EventObject} e
9310 "rowcontextmenu" : true,
9312 * @event cellcontextmenu
9313 * Fires when a cell is right clicked
9314 * @param {Roo.bootstrap.Table} this
9315 * @param {Number} rowIndex
9316 * @param {Number} cellIndex
9317 * @param {Roo.EventObject} e
9319 "cellcontextmenu" : true,
9321 * @event headercontextmenu
9322 * Fires when a header is right clicked
9323 * @param {Roo.bootstrap.Table} this
9324 * @param {Number} columnIndex
9325 * @param {Roo.EventObject} e
9327 "headercontextmenu" : true,
9330 * The raw mousedown event for the entire grid.
9331 * @param {Roo.EventObject} e
9338 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
9356 enableColumnResize: true,
9357 disableAutoSize: false,
9359 rowSelection : false,
9360 cellSelection : false,
9363 minColumnWidth : 50,
9365 // Roo.Element - the tbody
9366 bodyEl: false, // <tbody> Roo.Element - thead element
9367 headEl: false, // <thead> Roo.Element - thead element
9368 resizeProxy : false, // proxy element for dragging?
9372 container: false, // used by gridpanel...
9378 auto_hide_footer : false,
9380 view: false, // actually points to this..
9382 getAutoCreate : function()
9384 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9391 // this get's auto added by panel.Grid
9392 if (this.scrollBody) {
9393 cfg.cls += ' table-body-fixed';
9396 cfg.cls += ' table-striped';
9400 cfg.cls += ' table-hover';
9402 if (this.bordered) {
9403 cfg.cls += ' table-bordered';
9405 if (this.condensed) {
9406 cfg.cls += ' table-condensed';
9409 if (this.responsive) {
9410 cfg.cls += ' table-responsive';
9414 cfg.cls+= ' ' +this.cls;
9420 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9423 if(this.store || this.cm){
9424 if(this.headerShow){
9425 cfg.cn.push(this.renderHeader());
9428 cfg.cn.push(this.renderBody());
9430 if(this.footerShow || this.footerRow){
9431 cfg.cn.push(this.renderFooter());
9434 // where does this come from?
9435 //cfg.cls+= ' TableGrid';
9438 return { cn : [ cfg ] };
9441 initEvents : function()
9443 if(!this.store || !this.cm){
9446 if (this.selModel) {
9447 this.selModel.initEvents();
9451 //Roo.log('initEvents with ds!!!!');
9453 this.bodyEl = this.el.select('tbody', true).first();
9454 this.headEl = this.el.select('thead', true).first();
9455 this.mainFoot = this.el.select('tfoot', true).first();
9460 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9461 e.on('click', this.sort, this);
9465 // why is this done????? = it breaks dialogs??
9466 //this.parent().el.setStyle('position', 'relative');
9470 this.footer.parentId = this.id;
9471 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9474 this.el.select('tfoot tr td').first().addClass('hide');
9479 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9482 this.store.on('load', this.onLoad, this);
9483 this.store.on('beforeload', this.onBeforeLoad, this);
9484 this.store.on('update', this.onUpdate, this);
9485 this.store.on('add', this.onAdd, this);
9486 this.store.on("clear", this.clear, this);
9488 this.el.on("contextmenu", this.onContextMenu, this);
9491 this.cm.on("headerchange", this.onHeaderChange, this);
9492 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9494 //?? does bodyEl get replaced on render?
9495 this.bodyEl.on("click", this.onClick, this);
9496 this.bodyEl.on("dblclick", this.onDblClick, this);
9497 this.bodyEl.on('scroll', this.onBodyScroll, this);
9499 // guessing mainbody will work - this relays usually caught by selmodel at present.
9500 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9503 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9506 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9507 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9512 // Compatibility with grid - we implement all the view features at present.
9513 getView : function()
9518 initCSS : function()
9520 if(this.disableAutoSize) {
9524 var cm = this.cm, styles = [];
9525 this.CSS.removeStyleSheet(this.id + '-cssrules');
9526 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9527 // we can honour xs/sm/md/xl as widths...
9528 // we first have to decide what widht we are currently at...
9529 var sz = Roo.getGridSize();
9533 var cols = []; // visable cols.
9535 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9536 var w = cm.getColumnWidth(i, false);
9538 cols.push( { rel : false, abs : 0 });
9542 cols.push( { rel : false, abs : w });
9544 last = i; // not really..
9547 var w = cm.getColumnWidth(i, sz);
9552 cols.push( { rel : w, abs : false });
9555 var avail = this.bodyEl.dom.clientWidth - total_abs;
9557 var unitWidth = Math.floor(avail / total);
9558 var rem = avail - (unitWidth * total);
9560 var hidden, width, pos = 0 , splithide , left;
9561 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9563 hidden = 'display:none;';
9565 width = 'width:0px;';
9567 if(!cm.isHidden(i)){
9571 // we can honour xs/sm/md/xl ?
9572 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9574 hidden = 'display:none;';
9576 // width should return a small number...
9578 w+=rem; // add the remaining with..
9581 left = "left:" + (pos -4) + "px;";
9582 width = "width:" + w+ "px;";
9585 if (this.responsive) {
9588 hidden = cm.isHidden(i) ? 'display:none;' : '';
9589 splithide = 'display: none;';
9592 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9595 splithide = 'display:none;';
9598 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9599 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9600 // this is the popover version..
9601 '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9606 //Roo.log(styles.join(''));
9607 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9613 onContextMenu : function(e, t)
9615 this.processEvent("contextmenu", e);
9618 processEvent : function(name, e)
9620 if (name != 'touchstart' ) {
9621 this.fireEvent(name, e);
9624 var t = e.getTarget();
9626 var cell = Roo.get(t);
9632 if(cell.findParent('tfoot', false, true)){
9636 if(cell.findParent('thead', false, true)){
9638 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9639 cell = Roo.get(t).findParent('th', false, true);
9641 Roo.log("failed to find th in thead?");
9642 Roo.log(e.getTarget());
9647 var cellIndex = cell.dom.cellIndex;
9649 var ename = name == 'touchstart' ? 'click' : name;
9650 this.fireEvent("header" + ename, this, cellIndex, e);
9655 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9656 cell = Roo.get(t).findParent('td', false, true);
9658 Roo.log("failed to find th in tbody?");
9659 Roo.log(e.getTarget());
9664 var row = cell.findParent('tr', false, true);
9665 var cellIndex = cell.dom.cellIndex;
9666 var rowIndex = row.dom.rowIndex - 1;
9670 this.fireEvent("row" + name, this, rowIndex, e);
9674 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9680 onMouseover : function(e, el)
9682 var cell = Roo.get(el);
9688 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9689 cell = cell.findParent('td', false, true);
9692 var row = cell.findParent('tr', false, true);
9693 var cellIndex = cell.dom.cellIndex;
9694 var rowIndex = row.dom.rowIndex - 1; // start from 0
9696 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9700 onMouseout : function(e, el)
9702 var cell = Roo.get(el);
9708 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9709 cell = cell.findParent('td', false, true);
9712 var row = cell.findParent('tr', false, true);
9713 var cellIndex = cell.dom.cellIndex;
9714 var rowIndex = row.dom.rowIndex - 1; // start from 0
9716 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9720 onClick : function(e, el)
9722 var cell = Roo.get(el);
9724 if(!cell || (!this.cellSelection && !this.rowSelection)){
9728 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9729 cell = cell.findParent('td', false, true);
9732 if(!cell || typeof(cell) == 'undefined'){
9736 var row = cell.findParent('tr', false, true);
9738 if(!row || typeof(row) == 'undefined'){
9742 var cellIndex = cell.dom.cellIndex;
9743 var rowIndex = this.getRowIndex(row);
9745 // why??? - should these not be based on SelectionModel?
9746 //if(this.cellSelection){
9747 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9750 //if(this.rowSelection){
9751 this.fireEvent('rowclick', this, row, rowIndex, e);
9756 onDblClick : function(e,el)
9758 var cell = Roo.get(el);
9760 if(!cell || (!this.cellSelection && !this.rowSelection)){
9764 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9765 cell = cell.findParent('td', false, true);
9768 if(!cell || typeof(cell) == 'undefined'){
9772 var row = cell.findParent('tr', false, true);
9774 if(!row || typeof(row) == 'undefined'){
9778 var cellIndex = cell.dom.cellIndex;
9779 var rowIndex = this.getRowIndex(row);
9781 if(this.cellSelection){
9782 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9785 if(this.rowSelection){
9786 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9789 findRowIndex : function(el)
9791 var cell = Roo.get(el);
9795 var row = cell.findParent('tr', false, true);
9797 if(!row || typeof(row) == 'undefined'){
9800 return this.getRowIndex(row);
9802 sort : function(e,el)
9804 var col = Roo.get(el);
9806 if(!col.hasClass('sortable')){
9810 var sort = col.attr('sort');
9813 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9817 this.store.sortInfo = {field : sort, direction : dir};
9820 Roo.log("calling footer first");
9821 this.footer.onClick('first');
9824 this.store.load({ params : { start : 0 } });
9828 renderHeader : function()
9836 this.totalWidth = 0;
9838 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9840 var config = cm.config[i];
9844 cls : 'x-hcol-' + i,
9847 html: cm.getColumnHeader(i)
9850 var tooltip = cm.getColumnTooltip(i);
9852 c.tooltip = tooltip;
9858 if(typeof(config.sortable) != 'undefined' && config.sortable){
9859 c.cls += ' sortable';
9860 c.html = '<i class="fa"></i>' + c.html;
9863 // could use BS4 hidden-..-down
9865 if(typeof(config.lgHeader) != 'undefined'){
9866 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9869 if(typeof(config.mdHeader) != 'undefined'){
9870 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9873 if(typeof(config.smHeader) != 'undefined'){
9874 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9877 if(typeof(config.xsHeader) != 'undefined'){
9878 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9885 if(typeof(config.tooltip) != 'undefined'){
9886 c.tooltip = config.tooltip;
9889 if(typeof(config.colspan) != 'undefined'){
9890 c.colspan = config.colspan;
9893 // hidden is handled by CSS now
9895 if(typeof(config.dataIndex) != 'undefined'){
9896 c.sort = config.dataIndex;
9901 if(typeof(config.align) != 'undefined' && config.align.length){
9902 c.style += ' text-align:' + config.align + ';';
9905 /* width is done in CSS
9906 *if(typeof(config.width) != 'undefined'){
9907 c.style += ' width:' + config.width + 'px;';
9908 this.totalWidth += config.width;
9910 this.totalWidth += 100; // assume minimum of 100 per column?
9914 if(typeof(config.cls) != 'undefined'){
9915 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9917 // this is the bit that doesnt reall work at all...
9919 if (this.responsive) {
9922 ['xs','sm','md','lg'].map(function(size){
9924 if(typeof(config[size]) == 'undefined'){
9928 if (!config[size]) { // 0 = hidden
9929 // BS 4 '0' is treated as hide that column and below.
9930 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9934 c.cls += ' col-' + size + '-' + config[size] + (
9935 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9943 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9954 renderBody : function()
9964 colspan : this.cm.getColumnCount()
9974 renderFooter : function()
9984 colspan : this.cm.getColumnCount()
9996 // Roo.log('ds onload');
10001 var ds = this.store;
10003 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10004 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
10005 if (_this.store.sortInfo) {
10007 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
10008 e.select('i', true).addClass(['fa-arrow-up']);
10011 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10012 e.select('i', true).addClass(['fa-arrow-down']);
10017 var tbody = this.bodyEl;
10019 if(ds.getCount() > 0){
10020 ds.data.each(function(d,rowIndex){
10021 var row = this.renderRow(cm, ds, rowIndex);
10023 tbody.createChild(row);
10027 if(row.cellObjects.length){
10028 Roo.each(row.cellObjects, function(r){
10029 _this.renderCellObject(r);
10034 } else if (this.empty_results.length) {
10035 this.el.mask(this.empty_results, 'no-spinner');
10038 var tfoot = this.el.select('tfoot', true).first();
10040 if(this.footerShow && !this.footerRow && this.auto_hide_footer && this.mainFoot){
10042 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10044 var total = this.ds.getTotalCount();
10046 if(this.footer.pageSize < total){
10047 this.mainFoot.show();
10051 if(!this.footerShow && this.footerRow) {
10058 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10059 var footer = typeof(cm.config[i].footer) == "function" ? cm.config[i].footer(ds, cm.config[i]) : cm.config[i].footer;
10062 cls : ' x-fcol-' + i,
10070 tfoot.dom.innerHTML = '';
10072 tfoot.createChild(tr);
10075 Roo.each(this.el.select('tbody td', true).elements, function(e){
10076 e.on('mouseover', _this.onMouseover, _this);
10079 Roo.each(this.el.select('tbody td', true).elements, function(e){
10080 e.on('mouseout', _this.onMouseout, _this);
10082 this.fireEvent('rowsrendered', this);
10086 this.initCSS(); /// resize cols
10092 onUpdate : function(ds,record)
10094 this.refreshRow(record);
10098 onRemove : function(ds, record, index, isUpdate){
10099 if(isUpdate !== true){
10100 this.fireEvent("beforerowremoved", this, index, record);
10102 var bt = this.bodyEl.dom;
10104 var rows = this.el.select('tbody > tr', true).elements;
10106 if(typeof(rows[index]) != 'undefined'){
10107 bt.removeChild(rows[index].dom);
10110 // if(bt.rows[index]){
10111 // bt.removeChild(bt.rows[index]);
10114 if(isUpdate !== true){
10115 //this.stripeRows(index);
10116 //this.syncRowHeights(index, index);
10118 this.fireEvent("rowremoved", this, index, record);
10122 onAdd : function(ds, records, rowIndex)
10124 //Roo.log('on Add called');
10125 // - note this does not handle multiple adding very well..
10126 var bt = this.bodyEl.dom;
10127 for (var i =0 ; i < records.length;i++) {
10128 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10129 //Roo.log(records[i]);
10130 //Roo.log(this.store.getAt(rowIndex+i));
10131 this.insertRow(this.store, rowIndex + i, false);
10138 refreshRow : function(record){
10139 var ds = this.store, index;
10140 if(typeof record == 'number'){
10142 record = ds.getAt(index);
10144 index = ds.indexOf(record);
10146 return; // should not happen - but seems to
10149 this.insertRow(ds, index, true);
10151 this.onRemove(ds, record, index+1, true);
10153 //this.syncRowHeights(index, index);
10155 this.fireEvent("rowupdated", this, index, record);
10157 // private - called by RowSelection
10158 onRowSelect : function(rowIndex){
10159 var row = this.getRowDom(rowIndex);
10160 row.addClass(['bg-info','info']);
10162 // private - called by RowSelection
10163 onRowDeselect : function(rowIndex)
10165 if (rowIndex < 0) {
10168 var row = this.getRowDom(rowIndex);
10169 row.removeClass(['bg-info','info']);
10172 * Focuses the specified row.
10173 * @param {Number} row The row index
10175 focusRow : function(row)
10177 //Roo.log('GridView.focusRow');
10178 var x = this.bodyEl.dom.scrollLeft;
10179 this.focusCell(row, 0, false);
10180 this.bodyEl.dom.scrollLeft = x;
10184 * Focuses the specified cell.
10185 * @param {Number} row The row index
10186 * @param {Number} col The column index
10187 * @param {Boolean} hscroll false to disable horizontal scrolling
10189 focusCell : function(row, col, hscroll)
10191 //Roo.log('GridView.focusCell');
10192 var el = this.ensureVisible(row, col, hscroll);
10193 // not sure what focusEL achives = it's a <a> pos relative
10194 //this.focusEl.alignTo(el, "tl-tl");
10196 // this.focusEl.focus();
10198 // this.focusEl.focus.defer(1, this.focusEl);
10203 * Scrolls the specified cell into view
10204 * @param {Number} row The row index
10205 * @param {Number} col The column index
10206 * @param {Boolean} hscroll false to disable horizontal scrolling
10208 ensureVisible : function(row, col, hscroll)
10210 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10211 //return null; //disable for testing.
10212 if(typeof row != "number"){
10213 row = row.rowIndex;
10215 if(row < 0 && row >= this.ds.getCount()){
10218 col = (col !== undefined ? col : 0);
10220 while(cm.isHidden(col)){
10224 var el = this.getCellDom(row, col);
10228 var c = this.bodyEl.dom;
10230 var ctop = parseInt(el.offsetTop, 10);
10231 var cleft = parseInt(el.offsetLeft, 10);
10232 var cbot = ctop + el.offsetHeight;
10233 var cright = cleft + el.offsetWidth;
10235 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10236 var ch = 0; //?? header is not withing the area?
10237 var stop = parseInt(c.scrollTop, 10);
10238 var sleft = parseInt(c.scrollLeft, 10);
10239 var sbot = stop + ch;
10240 var sright = sleft + c.clientWidth;
10242 Roo.log('GridView.ensureVisible:' +
10244 ' c.clientHeight:' + c.clientHeight +
10245 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10253 c.scrollTop = ctop;
10254 //Roo.log("set scrolltop to ctop DISABLE?");
10255 }else if(cbot > sbot){
10256 //Roo.log("set scrolltop to cbot-ch");
10257 c.scrollTop = cbot-ch;
10260 if(hscroll !== false){
10262 c.scrollLeft = cleft;
10263 }else if(cright > sright){
10264 c.scrollLeft = cright-c.clientWidth;
10272 insertRow : function(dm, rowIndex, isUpdate){
10275 this.fireEvent("beforerowsinserted", this, rowIndex);
10277 //var s = this.getScrollState();
10278 var row = this.renderRow(this.cm, this.store, rowIndex);
10279 // insert before rowIndex..
10280 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10284 if(row.cellObjects.length){
10285 Roo.each(row.cellObjects, function(r){
10286 _this.renderCellObject(r);
10291 this.fireEvent("rowsinserted", this, rowIndex);
10292 //this.syncRowHeights(firstRow, lastRow);
10293 //this.stripeRows(firstRow);
10300 getRowDom : function(rowIndex)
10302 var rows = this.el.select('tbody > tr', true).elements;
10304 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10307 getCellDom : function(rowIndex, colIndex)
10309 var row = this.getRowDom(rowIndex);
10310 if (row === false) {
10313 var cols = row.select('td', true).elements;
10314 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10318 // returns the object tree for a tr..
10321 renderRow : function(cm, ds, rowIndex)
10323 var d = ds.getAt(rowIndex);
10327 cls : 'x-row-' + rowIndex,
10331 var cellObjects = [];
10333 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10334 var config = cm.config[i];
10336 var renderer = cm.getRenderer(i);
10340 if(typeof(renderer) !== 'undefined'){
10341 value = renderer.call(config, d.data[cm.getDataIndex(i)], false, d);
10343 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10344 // and are rendered into the cells after the row is rendered - using the id for the element.
10346 if(typeof(value) === 'object'){
10356 rowIndex : rowIndex,
10361 this.fireEvent('rowclass', this, rowcfg);
10365 // this might end up displaying HTML?
10366 // this is too messy... - better to only do it on columsn you know are going to be too long
10367 //tooltip : (typeof(value) === 'object') ? '' : value,
10368 cls : rowcfg.rowClass + ' x-col-' + i,
10370 html: (typeof(value) === 'object') ? '' : value
10377 if(typeof(config.colspan) != 'undefined'){
10378 td.colspan = config.colspan;
10383 if(typeof(config.align) != 'undefined' && config.align.length){
10384 td.style += ' text-align:' + config.align + ';';
10386 if(typeof(config.valign) != 'undefined' && config.valign.length){
10387 td.style += ' vertical-align:' + config.valign + ';';
10390 if(typeof(config.width) != 'undefined'){
10391 td.style += ' width:' + config.width + 'px;';
10395 if(typeof(config.cursor) != 'undefined'){
10396 td.style += ' cursor:' + config.cursor + ';';
10399 if(typeof(config.cls) != 'undefined'){
10400 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10402 if (this.responsive) {
10403 ['xs','sm','md','lg'].map(function(size){
10405 if(typeof(config[size]) == 'undefined'){
10411 if (!config[size]) { // 0 = hidden
10412 // BS 4 '0' is treated as hide that column and below.
10413 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10417 td.cls += ' col-' + size + '-' + config[size] + (
10418 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
10428 row.cellObjects = cellObjects;
10436 onBeforeLoad : function()
10438 this.el.unmask(); // if needed.
10445 this.el.select('tbody', true).first().dom.innerHTML = '';
10448 * Show or hide a row.
10449 * @param {Number} rowIndex to show or hide
10450 * @param {Boolean} state hide
10452 setRowVisibility : function(rowIndex, state)
10454 var bt = this.bodyEl.dom;
10456 var rows = this.el.select('tbody > tr', true).elements;
10458 if(typeof(rows[rowIndex]) == 'undefined'){
10461 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10466 getSelectionModel : function(){
10467 if(!this.selModel){
10468 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10470 return this.selModel;
10473 * Render the Roo.bootstrap object from renderder
10475 renderCellObject : function(r)
10479 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10481 var t = r.cfg.render(r.container);
10484 Roo.each(r.cfg.cn, function(c){
10486 container: t.getChildContainer(),
10489 _this.renderCellObject(child);
10494 * get the Row Index from a dom element.
10495 * @param {Roo.Element} row The row to look for
10496 * @returns {Number} the row
10498 getRowIndex : function(row)
10502 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10513 * get the header TH element for columnIndex
10514 * @param {Number} columnIndex
10515 * @returns {Roo.Element}
10517 getHeaderIndex: function(colIndex)
10519 var cols = this.headEl.select('th', true).elements;
10520 return cols[colIndex];
10523 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10524 * @param {domElement} cell to look for
10525 * @returns {Number} the column
10527 getCellIndex : function(cell)
10529 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10531 return parseInt(id[1], 10);
10536 * Returns the grid's underlying element = used by panel.Grid
10537 * @return {Element} The element
10539 getGridEl : function(){
10543 * Forces a resize - used by panel.Grid
10544 * @return {Element} The element
10546 autoSize : function()
10548 if(this.disableAutoSize) {
10551 //var ctr = Roo.get(this.container.dom.parentElement);
10552 var ctr = Roo.get(this.el.dom);
10554 var thd = this.getGridEl().select('thead',true).first();
10555 var tbd = this.getGridEl().select('tbody', true).first();
10556 var tfd = this.getGridEl().select('tfoot', true).first();
10558 var cw = ctr.getWidth();
10559 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10563 tbd.setWidth(ctr.getWidth());
10564 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10565 // this needs fixing for various usage - currently only hydra job advers I think..
10567 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10569 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10572 cw = Math.max(cw, this.totalWidth);
10573 this.getGridEl().select('tbody tr',true).setWidth(cw);
10576 // resize 'expandable coloumn?
10578 return; // we doe not have a view in this design..
10581 onBodyScroll: function()
10583 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10585 this.headEl.setStyle({
10586 'position' : 'relative',
10587 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10593 var scrollHeight = this.bodyEl.dom.scrollHeight;
10595 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10597 var height = this.bodyEl.getHeight();
10599 if(scrollHeight - height == scrollTop) {
10601 var total = this.ds.getTotalCount();
10603 if(this.footer.cursor + this.footer.pageSize < total){
10605 this.footer.ds.load({
10607 start : this.footer.cursor + this.footer.pageSize,
10608 limit : this.footer.pageSize
10617 onColumnSplitterMoved : function(i, diff)
10619 this.userResized = true;
10621 var cm = this.colModel;
10623 var w = this.getHeaderIndex(i).getWidth() + diff;
10626 cm.setColumnWidth(i, w, true);
10628 //var cid = cm.getColumnId(i); << not used in this version?
10629 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10631 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10632 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10633 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10635 //this.updateSplitters();
10636 //this.layout(); << ??
10637 this.fireEvent("columnresize", i, w);
10639 onHeaderChange : function()
10641 var header = this.renderHeader();
10642 var table = this.el.select('table', true).first();
10644 this.headEl.remove();
10645 this.headEl = table.createChild(header, this.bodyEl, false);
10647 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10648 e.on('click', this.sort, this);
10651 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10652 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10657 onHiddenChange : function(colModel, colIndex, hidden)
10660 this.cm.setHidden()
10661 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10662 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10664 this.CSS.updateRule(thSelector, "display", "");
10665 this.CSS.updateRule(tdSelector, "display", "");
10668 this.CSS.updateRule(thSelector, "display", "none");
10669 this.CSS.updateRule(tdSelector, "display", "none");
10672 // onload calls initCSS()
10673 this.onHeaderChange();
10677 setColumnWidth: function(col_index, width)
10679 // width = "md-2 xs-2..."
10680 if(!this.colModel.config[col_index]) {
10684 var w = width.split(" ");
10686 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10688 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10691 for(var j = 0; j < w.length; j++) {
10697 var size_cls = w[j].split("-");
10699 if(!Number.isInteger(size_cls[1] * 1)) {
10703 if(!this.colModel.config[col_index][size_cls[0]]) {
10707 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10711 h_row[0].classList.replace(
10712 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10713 "col-"+size_cls[0]+"-"+size_cls[1]
10716 for(var i = 0; i < rows.length; i++) {
10718 var size_cls = w[j].split("-");
10720 if(!Number.isInteger(size_cls[1] * 1)) {
10724 if(!this.colModel.config[col_index][size_cls[0]]) {
10728 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10732 rows[i].classList.replace(
10733 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10734 "col-"+size_cls[0]+"-"+size_cls[1]
10738 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10743 // currently only used to find the split on drag..
10744 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10749 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10750 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10759 * @class Roo.bootstrap.TableCell
10760 * @extends Roo.bootstrap.Component
10761 * @children Roo.bootstrap.Component
10762 * @parent Roo.bootstrap.TableRow
10763 * Bootstrap TableCell class
10765 * @cfg {String} html cell contain text
10766 * @cfg {String} cls cell class
10767 * @cfg {String} tag cell tag (td|th) default td
10768 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10769 * @cfg {String} align Aligns the content in a cell
10770 * @cfg {String} axis Categorizes cells
10771 * @cfg {String} bgcolor Specifies the background color of a cell
10772 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10773 * @cfg {Number} colspan Specifies the number of columns a cell should span
10774 * @cfg {String} headers Specifies one or more header cells a cell is related to
10775 * @cfg {Number} height Sets the height of a cell
10776 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10777 * @cfg {Number} rowspan Sets the number of rows a cell should span
10778 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10779 * @cfg {String} valign Vertical aligns the content in a cell
10780 * @cfg {Number} width Specifies the width of a cell
10783 * Create a new TableCell
10784 * @param {Object} config The config object
10787 Roo.bootstrap.TableCell = function(config){
10788 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10791 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10811 getAutoCreate : function(){
10812 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10819 cfg.tag = this.tag;
10832 cfg.align=this.align
10837 if (this.bgcolor) {
10838 cfg.bgcolor=this.bgcolor
10840 if (this.charoff) {
10841 cfg.charoff=this.charoff
10843 if (this.colspan) {
10844 cfg.colspan=this.colspan
10846 if (this.headers) {
10847 cfg.headers=this.headers
10850 cfg.height=this.height
10853 cfg.nowrap=this.nowrap
10855 if (this.rowspan) {
10856 cfg.rowspan=this.rowspan
10859 cfg.scope=this.scope
10862 cfg.valign=this.valign
10865 cfg.width=this.width
10884 * @class Roo.bootstrap.TableRow
10885 * @extends Roo.bootstrap.Component
10886 * @children Roo.bootstrap.TableCell
10887 * @parent Roo.bootstrap.TableBody
10888 * Bootstrap TableRow class
10889 * @cfg {String} cls row class
10890 * @cfg {String} align Aligns the content in a table row
10891 * @cfg {String} bgcolor Specifies a background color for a table row
10892 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10893 * @cfg {String} valign Vertical aligns the content in a table row
10896 * Create a new TableRow
10897 * @param {Object} config The config object
10900 Roo.bootstrap.TableRow = function(config){
10901 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10904 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10912 getAutoCreate : function(){
10913 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10920 cfg.cls = this.cls;
10923 cfg.align = this.align;
10926 cfg.bgcolor = this.bgcolor;
10929 cfg.charoff = this.charoff;
10932 cfg.valign = this.valign;
10950 * @class Roo.bootstrap.TableBody
10951 * @extends Roo.bootstrap.Component
10952 * @children Roo.bootstrap.TableRow
10953 * @parent Roo.bootstrap.Table
10954 * Bootstrap TableBody class
10955 * @cfg {String} cls element class
10956 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10957 * @cfg {String} align Aligns the content inside the element
10958 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10959 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10962 * Create a new TableBody
10963 * @param {Object} config The config object
10966 Roo.bootstrap.TableBody = function(config){
10967 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10970 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10978 getAutoCreate : function(){
10979 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10989 cfg.tag = this.tag;
10993 cfg.align = this.align;
10996 cfg.charoff = this.charoff;
10999 cfg.valign = this.valign;
11006 // initEvents : function()
11009 // if(!this.store){
11013 // this.store = Roo.factory(this.store, Roo.data);
11014 // this.store.on('load', this.onLoad, this);
11016 // this.store.load();
11020 // onLoad: function ()
11022 // this.fireEvent('load', this);
11032 * Ext JS Library 1.1.1
11033 * Copyright(c) 2006-2007, Ext JS, LLC.
11035 * Originally Released Under LGPL - original licence link has changed is not relivant.
11038 * <script type="text/javascript">
11041 // as we use this in bootstrap.
11042 Roo.namespace('Roo.form');
11044 * @class Roo.form.Action
11045 * Internal Class used to handle form actions
11047 * @param {Roo.form.BasicForm} el The form element or its id
11048 * @param {Object} config Configuration options
11053 // define the action interface
11054 Roo.form.Action = function(form, options){
11056 this.options = options || {};
11059 * Client Validation Failed
11062 Roo.form.Action.CLIENT_INVALID = 'client';
11064 * Server Validation Failed
11067 Roo.form.Action.SERVER_INVALID = 'server';
11069 * Connect to Server Failed
11072 Roo.form.Action.CONNECT_FAILURE = 'connect';
11074 * Reading Data from Server Failed
11077 Roo.form.Action.LOAD_FAILURE = 'load';
11079 Roo.form.Action.prototype = {
11081 failureType : undefined,
11082 response : undefined,
11083 result : undefined,
11085 // interface method
11086 run : function(options){
11090 // interface method
11091 success : function(response){
11095 // interface method
11096 handleResponse : function(response){
11100 // default connection failure
11101 failure : function(response){
11103 this.response = response;
11104 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11105 this.form.afterAction(this, false);
11108 processResponse : function(response){
11109 this.response = response;
11110 if(!response.responseText){
11113 this.result = this.handleResponse(response);
11114 return this.result;
11117 // utility functions used internally
11118 getUrl : function(appendParams){
11119 var url = this.options.url || this.form.url || this.form.el.dom.action;
11121 var p = this.getParams();
11123 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11129 getMethod : function(){
11130 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11133 getParams : function(){
11134 var bp = this.form.baseParams;
11135 var p = this.options.params;
11137 if(typeof p == "object"){
11138 p = Roo.urlEncode(Roo.applyIf(p, bp));
11139 }else if(typeof p == 'string' && bp){
11140 p += '&' + Roo.urlEncode(bp);
11143 p = Roo.urlEncode(bp);
11148 createCallback : function(){
11150 success: this.success,
11151 failure: this.failure,
11153 timeout: (this.form.timeout*1000),
11154 upload: this.form.fileUpload ? this.success : undefined
11159 Roo.form.Action.Submit = function(form, options){
11160 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11163 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11166 haveProgress : false,
11167 uploadComplete : false,
11169 // uploadProgress indicator.
11170 uploadProgress : function()
11172 if (!this.form.progressUrl) {
11176 if (!this.haveProgress) {
11177 Roo.MessageBox.progress("Uploading", "Uploading");
11179 if (this.uploadComplete) {
11180 Roo.MessageBox.hide();
11184 this.haveProgress = true;
11186 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11188 var c = new Roo.data.Connection();
11190 url : this.form.progressUrl,
11195 success : function(req){
11196 //console.log(data);
11200 rdata = Roo.decode(req.responseText)
11202 Roo.log("Invalid data from server..");
11206 if (!rdata || !rdata.success) {
11208 Roo.MessageBox.alert(Roo.encode(rdata));
11211 var data = rdata.data;
11213 if (this.uploadComplete) {
11214 Roo.MessageBox.hide();
11219 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11220 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11223 this.uploadProgress.defer(2000,this);
11226 failure: function(data) {
11227 Roo.log('progress url failed ');
11238 // run get Values on the form, so it syncs any secondary forms.
11239 this.form.getValues();
11241 var o = this.options;
11242 var method = this.getMethod();
11243 var isPost = method == 'POST';
11244 if(o.clientValidation === false || this.form.isValid()){
11246 if (this.form.progressUrl) {
11247 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11248 (new Date() * 1) + '' + Math.random());
11253 Roo.Ajax.request(Roo.apply(this.createCallback(), {
11254 form:this.form.el.dom,
11255 url:this.getUrl(!isPost),
11257 params:isPost ? this.getParams() : null,
11258 isUpload: this.form.fileUpload,
11259 formData : this.form.formData
11262 this.uploadProgress();
11264 }else if (o.clientValidation !== false){ // client validation failed
11265 this.failureType = Roo.form.Action.CLIENT_INVALID;
11266 this.form.afterAction(this, false);
11270 success : function(response)
11272 this.uploadComplete= true;
11273 if (this.haveProgress) {
11274 Roo.MessageBox.hide();
11278 var result = this.processResponse(response);
11279 if(result === true || result.success){
11280 this.form.afterAction(this, true);
11284 this.form.markInvalid(result.errors);
11285 this.failureType = Roo.form.Action.SERVER_INVALID;
11287 this.form.afterAction(this, false);
11289 failure : function(response)
11291 this.uploadComplete= true;
11292 if (this.haveProgress) {
11293 Roo.MessageBox.hide();
11296 this.response = response;
11297 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11298 this.form.afterAction(this, false);
11301 handleResponse : function(response){
11302 if(this.form.errorReader){
11303 var rs = this.form.errorReader.read(response);
11306 for(var i = 0, len = rs.records.length; i < len; i++) {
11307 var r = rs.records[i];
11308 errors[i] = r.data;
11311 if(errors.length < 1){
11315 success : rs.success,
11321 var rt = response.responseText;
11322 if (rt.match(/^\<!--\[CDATA\[/)) {
11323 rt = rt.replace(/^\<!--\[CDATA\[/,'');
11324 rt = rt.replace(/\]\]--\>$/,'');
11327 ret = Roo.decode(rt);
11331 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11341 Roo.form.Action.Load = function(form, options){
11342 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11343 this.reader = this.form.reader;
11346 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11351 Roo.Ajax.request(Roo.apply(
11352 this.createCallback(), {
11353 method:this.getMethod(),
11354 url:this.getUrl(false),
11355 params:this.getParams()
11359 success : function(response){
11361 var result = this.processResponse(response);
11362 if(result === true || !result.success || !result.data){
11363 this.failureType = Roo.form.Action.LOAD_FAILURE;
11364 this.form.afterAction(this, false);
11367 this.form.clearInvalid();
11368 this.form.setValues(result.data);
11369 this.form.afterAction(this, true);
11372 handleResponse : function(response){
11373 if(this.form.reader){
11374 var rs = this.form.reader.read(response);
11375 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11377 success : rs.success,
11381 return Roo.decode(response.responseText);
11385 Roo.form.Action.ACTION_TYPES = {
11386 'load' : Roo.form.Action.Load,
11387 'submit' : Roo.form.Action.Submit
11396 * @class Roo.bootstrap.form.Form
11397 * @extends Roo.bootstrap.Component
11398 * @children Roo.bootstrap.Component
11399 * Bootstrap Form class
11400 * @cfg {String} method GET | POST (default POST)
11401 * @cfg {String} labelAlign top | left (default top)
11402 * @cfg {String} align left | right - for navbars
11403 * @cfg {Boolean} loadMask load mask when submit (default true)
11407 * Create a new Form
11408 * @param {Object} config The config object
11412 Roo.bootstrap.form.Form = function(config){
11414 Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11416 Roo.bootstrap.form.Form.popover.apply();
11420 * @event clientvalidation
11421 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11422 * @param {Form} this
11423 * @param {Boolean} valid true if the form has passed client-side validation
11425 clientvalidation: true,
11427 * @event beforeaction
11428 * Fires before any action is performed. Return false to cancel the action.
11429 * @param {Form} this
11430 * @param {Action} action The action to be performed
11432 beforeaction: true,
11434 * @event actionfailed
11435 * Fires when an action fails.
11436 * @param {Form} this
11437 * @param {Action} action The action that failed
11439 actionfailed : true,
11441 * @event actioncomplete
11442 * Fires when an action is completed.
11443 * @param {Form} this
11444 * @param {Action} action The action that completed
11446 actioncomplete : true
11450 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component, {
11453 * @cfg {String} method
11454 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11458 * @cfg {String} url
11459 * The URL to use for form actions if one isn't supplied in the action options.
11462 * @cfg {Boolean} fileUpload
11463 * Set to true if this form is a file upload.
11467 * @cfg {Object} baseParams
11468 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11472 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11476 * @cfg {Sting} align (left|right) for navbar forms
11481 activeAction : null,
11484 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11485 * element by passing it or its id or mask the form itself by passing in true.
11488 waitMsgTarget : false,
11493 * @cfg {Boolean} errorMask (true|false) default false
11498 * @cfg {Number} maskOffset Default 100
11503 * @cfg {Boolean} maskBody
11507 getAutoCreate : function(){
11511 method : this.method || 'POST',
11512 id : this.id || Roo.id(),
11515 if (this.parent().xtype.match(/^Nav/)) {
11516 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11520 if (this.labelAlign == 'left' ) {
11521 cfg.cls += ' form-horizontal';
11527 initEvents : function()
11529 this.el.on('submit', this.onSubmit, this);
11530 // this was added as random key presses on the form where triggering form submit.
11531 this.el.on('keypress', function(e) {
11532 if (e.getCharCode() != 13) {
11535 // we might need to allow it for textareas.. and some other items.
11536 // check e.getTarget().
11538 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11542 Roo.log("keypress blocked");
11544 e.preventDefault();
11550 onSubmit : function(e){
11555 * Returns true if client-side validation on the form is successful.
11558 isValid : function(){
11559 var items = this.getItems();
11561 var target = false;
11563 items.each(function(f){
11569 Roo.log('invalid field: ' + f.name);
11573 if(!target && f.el.isVisible(true)){
11579 if(this.errorMask && !valid){
11580 Roo.bootstrap.form.Form.popover.mask(this, target);
11587 * Returns true if any fields in this form have changed since their original load.
11590 isDirty : function(){
11592 var items = this.getItems();
11593 items.each(function(f){
11603 * Performs a predefined action (submit or load) or custom actions you define on this form.
11604 * @param {String} actionName The name of the action type
11605 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11606 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11607 * accept other config options):
11609 Property Type Description
11610 ---------------- --------------- ----------------------------------------------------------------------------------
11611 url String The url for the action (defaults to the form's url)
11612 method String The form method to use (defaults to the form's method, or POST if not defined)
11613 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11614 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11615 validate the form on the client (defaults to false)
11617 * @return {BasicForm} this
11619 doAction : function(action, options){
11620 if(typeof action == 'string'){
11621 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11623 if(this.fireEvent('beforeaction', this, action) !== false){
11624 this.beforeAction(action);
11625 action.run.defer(100, action);
11631 beforeAction : function(action){
11632 var o = action.options;
11637 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11639 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11642 // not really supported yet.. ??
11644 //if(this.waitMsgTarget === true){
11645 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11646 //}else if(this.waitMsgTarget){
11647 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11648 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11650 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11656 afterAction : function(action, success){
11657 this.activeAction = null;
11658 var o = action.options;
11663 Roo.get(document.body).unmask();
11669 //if(this.waitMsgTarget === true){
11670 // this.el.unmask();
11671 //}else if(this.waitMsgTarget){
11672 // this.waitMsgTarget.unmask();
11674 // Roo.MessageBox.updateProgress(1);
11675 // Roo.MessageBox.hide();
11682 Roo.callback(o.success, o.scope, [this, action]);
11683 this.fireEvent('actioncomplete', this, action);
11687 // failure condition..
11688 // we have a scenario where updates need confirming.
11689 // eg. if a locking scenario exists..
11690 // we look for { errors : { needs_confirm : true }} in the response.
11692 (typeof(action.result) != 'undefined') &&
11693 (typeof(action.result.errors) != 'undefined') &&
11694 (typeof(action.result.errors.needs_confirm) != 'undefined')
11697 Roo.log("not supported yet");
11700 Roo.MessageBox.confirm(
11701 "Change requires confirmation",
11702 action.result.errorMsg,
11707 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11717 Roo.callback(o.failure, o.scope, [this, action]);
11718 // show an error message if no failed handler is set..
11719 if (!this.hasListener('actionfailed')) {
11720 Roo.log("need to add dialog support");
11722 Roo.MessageBox.alert("Error",
11723 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11724 action.result.errorMsg :
11725 "Saving Failed, please check your entries or try again"
11730 this.fireEvent('actionfailed', this, action);
11735 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11736 * @param {String} id The value to search for
11739 findField : function(id){
11740 var items = this.getItems();
11741 var field = items.get(id);
11743 items.each(function(f){
11744 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11751 return field || null;
11754 * Mark fields in this form invalid in bulk.
11755 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11756 * @return {BasicForm} this
11758 markInvalid : function(errors){
11759 if(errors instanceof Array){
11760 for(var i = 0, len = errors.length; i < len; i++){
11761 var fieldError = errors[i];
11762 var f = this.findField(fieldError.id);
11764 f.markInvalid(fieldError.msg);
11770 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11771 field.markInvalid(errors[id]);
11775 //Roo.each(this.childForms || [], function (f) {
11776 // f.markInvalid(errors);
11783 * Set values for fields in this form in bulk.
11784 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11785 * @return {BasicForm} this
11787 setValues : function(values){
11788 if(values instanceof Array){ // array of objects
11789 for(var i = 0, len = values.length; i < len; i++){
11791 var f = this.findField(v.id);
11793 f.setValue(v.value);
11794 if(this.trackResetOnLoad){
11795 f.originalValue = f.getValue();
11799 }else{ // object hash
11802 if(typeof values[id] != 'function' && (field = this.findField(id))){
11804 if (field.setFromData &&
11805 field.valueField &&
11806 field.displayField &&
11807 // combos' with local stores can
11808 // be queried via setValue()
11809 // to set their value..
11810 (field.store && !field.store.isLocal)
11814 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11815 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11816 field.setFromData(sd);
11818 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11820 field.setFromData(values);
11823 field.setValue(values[id]);
11827 if(this.trackResetOnLoad){
11828 field.originalValue = field.getValue();
11834 //Roo.each(this.childForms || [], function (f) {
11835 // f.setValues(values);
11842 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11843 * they are returned as an array.
11844 * @param {Boolean} asString
11847 getValues : function(asString){
11848 //if (this.childForms) {
11849 // copy values from the child forms
11850 // Roo.each(this.childForms, function (f) {
11851 // this.setValues(f.getValues());
11857 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11858 if(asString === true){
11861 return Roo.urlDecode(fs);
11865 * Returns the fields in this form as an object with key/value pairs.
11866 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11869 getFieldValues : function(with_hidden)
11871 var items = this.getItems();
11873 items.each(function(f){
11875 if (!f.getName()) {
11879 var v = f.getValue();
11881 if (f.inputType =='radio') {
11882 if (typeof(ret[f.getName()]) == 'undefined') {
11883 ret[f.getName()] = ''; // empty..
11886 if (!f.el.dom.checked) {
11890 v = f.el.dom.value;
11894 if(f.xtype == 'MoneyField'){
11895 ret[f.currencyName] = f.getCurrency();
11898 // not sure if this supported any more..
11899 if ((typeof(v) == 'object') && f.getRawValue) {
11900 v = f.getRawValue() ; // dates..
11902 // combo boxes where name != hiddenName...
11903 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11904 ret[f.name] = f.getRawValue();
11906 ret[f.getName()] = v;
11913 * Clears all invalid messages in this form.
11914 * @return {BasicForm} this
11916 clearInvalid : function(){
11917 var items = this.getItems();
11919 items.each(function(f){
11927 * Resets this form.
11928 * @return {BasicForm} this
11930 reset : function(){
11931 var items = this.getItems();
11932 items.each(function(f){
11936 Roo.each(this.childForms || [], function (f) {
11944 getItems : function()
11946 var r=new Roo.util.MixedCollection(false, function(o){
11947 return o.id || (o.id = Roo.id());
11949 var iter = function(el) {
11956 Roo.each(el.items,function(e) {
11965 hideFields : function(items)
11967 Roo.each(items, function(i){
11969 var f = this.findField(i);
11980 showFields : function(items)
11982 Roo.each(items, function(i){
11984 var f = this.findField(i);
11997 Roo.apply(Roo.bootstrap.form.Form, {
12013 intervalID : false,
12019 if(this.isApplied){
12024 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
12025 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
12026 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
12027 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
12030 this.maskEl.top.enableDisplayMode("block");
12031 this.maskEl.left.enableDisplayMode("block");
12032 this.maskEl.bottom.enableDisplayMode("block");
12033 this.maskEl.right.enableDisplayMode("block");
12035 this.toolTip = new Roo.bootstrap.Tooltip({
12036 cls : 'roo-form-error-popover',
12038 'left' : ['r-l', [-2,0], 'right'],
12039 'right' : ['l-r', [2,0], 'left'],
12040 'bottom' : ['tl-bl', [0,2], 'top'],
12041 'top' : [ 'bl-tl', [0,-2], 'bottom']
12045 this.toolTip.render(Roo.get(document.body));
12047 this.toolTip.el.enableDisplayMode("block");
12049 Roo.get(document.body).on('click', function(){
12053 Roo.get(document.body).on('touchstart', function(){
12057 this.isApplied = true
12060 mask : function(form, target)
12064 this.target = target;
12066 if(!this.form.errorMask || !target.el){
12070 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12072 Roo.log(scrollable);
12074 var ot = this.target.el.calcOffsetsTo(scrollable);
12076 var scrollTo = ot[1] - this.form.maskOffset;
12078 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12080 scrollable.scrollTo('top', scrollTo);
12082 var box = this.target.el.getBox();
12084 var zIndex = Roo.bootstrap.Modal.zIndex++;
12087 this.maskEl.top.setStyle('position', 'absolute');
12088 this.maskEl.top.setStyle('z-index', zIndex);
12089 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12090 this.maskEl.top.setLeft(0);
12091 this.maskEl.top.setTop(0);
12092 this.maskEl.top.show();
12094 this.maskEl.left.setStyle('position', 'absolute');
12095 this.maskEl.left.setStyle('z-index', zIndex);
12096 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12097 this.maskEl.left.setLeft(0);
12098 this.maskEl.left.setTop(box.y - this.padding);
12099 this.maskEl.left.show();
12101 this.maskEl.bottom.setStyle('position', 'absolute');
12102 this.maskEl.bottom.setStyle('z-index', zIndex);
12103 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12104 this.maskEl.bottom.setLeft(0);
12105 this.maskEl.bottom.setTop(box.bottom + this.padding);
12106 this.maskEl.bottom.show();
12108 this.maskEl.right.setStyle('position', 'absolute');
12109 this.maskEl.right.setStyle('z-index', zIndex);
12110 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12111 this.maskEl.right.setLeft(box.right + this.padding);
12112 this.maskEl.right.setTop(box.y - this.padding);
12113 this.maskEl.right.show();
12115 this.toolTip.bindEl = this.target.el;
12117 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12119 var tip = this.target.blankText;
12121 if(this.target.getValue() !== '' ) {
12123 if (this.target.invalidText.length) {
12124 tip = this.target.invalidText;
12125 } else if (this.target.regexText.length){
12126 tip = this.target.regexText;
12130 this.toolTip.show(tip);
12132 this.intervalID = window.setInterval(function() {
12133 Roo.bootstrap.form.Form.popover.unmask();
12136 window.onwheel = function(){ return false;};
12138 (function(){ this.isMasked = true; }).defer(500, this);
12142 unmask : function()
12144 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12148 this.maskEl.top.setStyle('position', 'absolute');
12149 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12150 this.maskEl.top.hide();
12152 this.maskEl.left.setStyle('position', 'absolute');
12153 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12154 this.maskEl.left.hide();
12156 this.maskEl.bottom.setStyle('position', 'absolute');
12157 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12158 this.maskEl.bottom.hide();
12160 this.maskEl.right.setStyle('position', 'absolute');
12161 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12162 this.maskEl.right.hide();
12164 this.toolTip.hide();
12166 this.toolTip.el.hide();
12168 window.onwheel = function(){ return true;};
12170 if(this.intervalID){
12171 window.clearInterval(this.intervalID);
12172 this.intervalID = false;
12175 this.isMasked = false;
12185 * Ext JS Library 1.1.1
12186 * Copyright(c) 2006-2007, Ext JS, LLC.
12188 * Originally Released Under LGPL - original licence link has changed is not relivant.
12191 * <script type="text/javascript">
12194 * @class Roo.form.VTypes
12195 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12198 Roo.form.VTypes = function(){
12199 // closure these in so they are only created once.
12200 var alpha = /^[a-zA-Z_]+$/;
12201 var alphanum = /^[a-zA-Z0-9_]+$/;
12202 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12203 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12205 // All these messages and functions are configurable
12208 * The function used to validate email addresses
12209 * @param {String} value The email address
12211 email : function(v){
12212 return email.test(v);
12215 * The error text to display when the email validation function returns false
12218 emailText : 'This field should be an e-mail address in the format "user@domain.com"',
12220 * The keystroke filter mask to be applied on email input
12223 emailMask : /[a-z0-9_\.\-@]/i,
12226 * The function used to validate URLs
12227 * @param {String} value The URL
12230 return url.test(v);
12233 * The error text to display when the url validation function returns false
12236 urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12239 * The function used to validate alpha values
12240 * @param {String} value The value
12242 alpha : function(v){
12243 return alpha.test(v);
12246 * The error text to display when the alpha validation function returns false
12249 alphaText : 'This field should only contain letters and _',
12251 * The keystroke filter mask to be applied on alpha input
12254 alphaMask : /[a-z_]/i,
12257 * The function used to validate alphanumeric values
12258 * @param {String} value The value
12260 alphanum : function(v){
12261 return alphanum.test(v);
12264 * The error text to display when the alphanumeric validation function returns false
12267 alphanumText : 'This field should only contain letters, numbers and _',
12269 * The keystroke filter mask to be applied on alphanumeric input
12272 alphanumMask : /[a-z0-9_]/i
12282 * @class Roo.bootstrap.form.Input
12283 * @extends Roo.bootstrap.Component
12284 * Bootstrap Input class
12285 * @cfg {Boolean} disabled is it disabled
12286 * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)
12287 * @cfg {String} name name of the input
12288 * @cfg {string} fieldLabel - the label associated
12289 * @cfg {string} placeholder - placeholder to put in text.
12290 * @cfg {string} before - input group add on before
12291 * @cfg {string} after - input group add on after
12292 * @cfg {string} size - (lg|sm) or leave empty..
12293 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12294 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12295 * @cfg {Number} md colspan out of 12 for computer-sized screens
12296 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12297 * @cfg {string} value default value of the input
12298 * @cfg {Number} labelWidth set the width of label
12299 * @cfg {Number} labellg set the width of label (1-12)
12300 * @cfg {Number} labelmd set the width of label (1-12)
12301 * @cfg {Number} labelsm set the width of label (1-12)
12302 * @cfg {Number} labelxs set the width of label (1-12)
12303 * @cfg {String} labelAlign (top|left)
12304 * @cfg {Boolean} readOnly Specifies that the field should be read-only
12305 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12306 * @cfg {String} indicatorpos (left|right) default left
12307 * @cfg {String} capture (user|camera) use for file input only. (default empty)
12308 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12309 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12310 * @cfg {Roo.bootstrap.Button} before Button to show before
12311 * @cfg {Roo.bootstrap.Button} afterButton to show before
12312 * @cfg {String} align (left|center|right) Default left
12313 * @cfg {Boolean} forceFeedback (true|false) Default false
12316 * Create a new Input
12317 * @param {Object} config The config object
12320 Roo.bootstrap.form.Input = function(config){
12322 Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12327 * Fires when this field receives input focus.
12328 * @param {Roo.form.Field} this
12333 * Fires when this field loses input focus.
12334 * @param {Roo.form.Field} this
12338 * @event specialkey
12339 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
12340 * {@link Roo.EventObject#getKey} to determine which key was pressed.
12341 * @param {Roo.form.Field} this
12342 * @param {Roo.EventObject} e The event object
12347 * Fires just before the field blurs if the field value has changed.
12348 * @param {Roo.form.Field} this
12349 * @param {Mixed} newValue The new value
12350 * @param {Mixed} oldValue The original value
12355 * Fires after the field has been marked as invalid.
12356 * @param {Roo.form.Field} this
12357 * @param {String} msg The validation message
12362 * Fires after the field has been validated with no errors.
12363 * @param {Roo.form.Field} this
12368 * Fires after the key up
12369 * @param {Roo.form.Field} this
12370 * @param {Roo.EventObject} e The event Object
12375 * Fires after the user pastes into input
12376 * @param {Roo.form.Field} this
12377 * @param {Roo.EventObject} e The event Object
12383 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component, {
12385 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12386 automatic validation (defaults to "keyup").
12388 validationEvent : "keyup",
12390 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12392 validateOnBlur : true,
12394 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12396 validationDelay : 250,
12398 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12400 focusClass : "x-form-focus", // not needed???
12404 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12406 invalidClass : "has-warning",
12409 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12411 validClass : "has-success",
12414 * @cfg {Boolean} hasFeedback (true|false) default true
12416 hasFeedback : true,
12419 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12421 invalidFeedbackClass : "glyphicon-warning-sign",
12424 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12426 validFeedbackClass : "glyphicon-ok",
12429 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12431 selectOnFocus : false,
12434 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12438 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12443 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12445 disableKeyFilter : false,
12448 * @cfg {Boolean} disabled True to disable the field (defaults to false).
12452 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12456 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12458 blankText : "Please complete this mandatory field",
12461 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12465 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12467 maxLength : Number.MAX_VALUE,
12469 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12471 minLengthText : "The minimum length for this field is {0}",
12473 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12475 maxLengthText : "The maximum length for this field is {0}",
12479 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12480 * If available, this function will be called only after the basic validators all return true, and will be passed the
12481 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12485 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12486 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12487 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
12491 * @cfg {String} regexText -- Depricated - use Invalid Text
12496 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12502 autocomplete: false,
12506 inputType : 'text',
12509 placeholder: false,
12514 preventMark: false,
12515 isFormField : true,
12518 labelAlign : false,
12521 formatedValue : false,
12522 forceFeedback : false,
12524 indicatorpos : 'left',
12534 parentLabelAlign : function()
12537 while (parent.parent()) {
12538 parent = parent.parent();
12539 if (typeof(parent.labelAlign) !='undefined') {
12540 return parent.labelAlign;
12547 getAutoCreate : function()
12554 if(this.inputType != 'hidden'){
12555 cfg.cls = 'form-group' //input-group
12561 type : this.inputType,
12562 value : this.value,
12563 cls : 'form-control',
12564 placeholder : this.placeholder || '',
12565 autocomplete : this.autocomplete || 'new-password'
12567 if (this.inputType == 'file') {
12568 input.style = 'overflow:hidden'; // why not in CSS?
12571 if(this.capture.length){
12572 input.capture = this.capture;
12575 if(this.accept.length){
12576 input.accept = this.accept + "/*";
12580 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12583 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12584 input.maxLength = this.maxLength;
12587 if (this.disabled) {
12588 input.disabled=true;
12591 if (this.readOnly) {
12592 input.readonly=true;
12596 input.name = this.name;
12600 input.cls += ' input-' + this.size;
12604 ['xs','sm','md','lg'].map(function(size){
12605 if (settings[size]) {
12606 cfg.cls += ' col-' + size + '-' + settings[size];
12610 var inputblock = input;
12614 cls: 'glyphicon form-control-feedback'
12617 if(this.hasFeedback && this.inputType != 'hidden'){
12620 cls : 'has-feedback',
12628 if (this.before || this.after) {
12631 cls : 'input-group',
12635 if (this.before && typeof(this.before) == 'string') {
12637 inputblock.cn.push({
12639 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12643 if (this.before && typeof(this.before) == 'object') {
12644 this.before = Roo.factory(this.before);
12646 inputblock.cn.push({
12648 cls : 'roo-input-before input-group-prepend input-group-' +
12649 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12653 inputblock.cn.push(input);
12655 if (this.after && typeof(this.after) == 'string') {
12656 inputblock.cn.push({
12658 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12662 if (this.after && typeof(this.after) == 'object') {
12663 this.after = Roo.factory(this.after);
12665 inputblock.cn.push({
12667 cls : 'roo-input-after input-group-append input-group-' +
12668 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12672 if(this.hasFeedback && this.inputType != 'hidden'){
12673 inputblock.cls += ' has-feedback';
12674 inputblock.cn.push(feedback);
12680 cfg = this.getAutoCreateLabel( cfg, inputblock );
12685 if (this.parentType === 'Navbar' && this.parent().bar) {
12686 cfg.cls += ' navbar-form';
12689 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12690 // on BS4 we do this only if not form
12691 cfg.cls += ' navbar-form';
12699 * autocreate the label - also used by textara... ?? and others?
12701 getAutoCreateLabel : function( cfg, inputblock )
12703 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12707 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12708 tooltip : 'This field is required'
12710 if (this.allowBlank ) {
12711 indicator.style = this.allowBlank ? ' display:none' : '';
12713 if (align ==='left' && this.fieldLabel.length) {
12715 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12722 cls : 'control-label col-form-label',
12723 html : this.fieldLabel
12734 var labelCfg = cfg.cn[1];
12735 var contentCfg = cfg.cn[2];
12737 if(this.indicatorpos == 'right'){
12742 cls : 'control-label col-form-label',
12746 html : this.fieldLabel
12760 labelCfg = cfg.cn[0];
12761 contentCfg = cfg.cn[1];
12765 if(this.labelWidth > 12){
12766 labelCfg.style = "width: " + this.labelWidth + 'px';
12769 if(this.labelWidth < 13 && this.labelmd == 0){
12770 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12773 if(this.labellg > 0){
12774 labelCfg.cls += ' col-lg-' + this.labellg;
12775 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12778 if(this.labelmd > 0){
12779 labelCfg.cls += ' col-md-' + this.labelmd;
12780 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12783 if(this.labelsm > 0){
12784 labelCfg.cls += ' col-sm-' + this.labelsm;
12785 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12788 if(this.labelxs > 0){
12789 labelCfg.cls += ' col-xs-' + this.labelxs;
12790 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12794 } else if ( this.fieldLabel.length) {
12801 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12802 tooltip : 'This field is required',
12803 style : this.allowBlank ? ' display:none' : ''
12807 //cls : 'input-group-addon',
12808 html : this.fieldLabel
12816 if(this.indicatorpos == 'right'){
12821 //cls : 'input-group-addon',
12822 html : this.fieldLabel
12827 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12828 tooltip : 'This field is required',
12829 style : this.allowBlank ? ' display:none' : ''
12853 * return the real input element.
12855 inputEl: function ()
12857 return this.el.select('input.form-control',true).first();
12860 tooltipEl : function()
12862 return this.inputEl();
12865 indicatorEl : function()
12867 if (Roo.bootstrap.version == 4) {
12868 return false; // not enabled in v4 yet.
12871 var indicator = this.el.select('i.roo-required-indicator',true).first();
12881 setDisabled : function(v)
12883 var i = this.inputEl().dom;
12885 i.removeAttribute('disabled');
12889 i.setAttribute('disabled','true');
12891 initEvents : function()
12894 this.inputEl().on("keydown" , this.fireKey, this);
12895 this.inputEl().on("focus", this.onFocus, this);
12896 this.inputEl().on("blur", this.onBlur, this);
12898 this.inputEl().relayEvent('keyup', this);
12899 this.inputEl().relayEvent('paste', this);
12901 this.indicator = this.indicatorEl();
12903 if(this.indicator){
12904 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12907 // reference to original value for reset
12908 this.originalValue = this.getValue();
12909 //Roo.form.TextField.superclass.initEvents.call(this);
12910 if(this.validationEvent == 'keyup'){
12911 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12912 this.inputEl().on('keyup', this.filterValidation, this);
12914 else if(this.validationEvent !== false){
12915 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12918 if(this.selectOnFocus){
12919 this.on("focus", this.preFocus, this);
12922 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12923 this.inputEl().on("keypress", this.filterKeys, this);
12925 this.inputEl().relayEvent('keypress', this);
12928 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12929 this.el.on("click", this.autoSize, this);
12932 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12933 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12936 if (typeof(this.before) == 'object') {
12937 this.before.render(this.el.select('.roo-input-before',true).first());
12939 if (typeof(this.after) == 'object') {
12940 this.after.render(this.el.select('.roo-input-after',true).first());
12943 this.inputEl().on('change', this.onChange, this);
12945 if(this.hasFeedback && this.inputType != 'hidden'){
12947 var feedback = this.el.select('.form-control-feedback', true).first();
12955 filterValidation : function(e){
12956 if(!e.isNavKeyPress()){
12957 this.validationTask.delay(this.validationDelay);
12961 * Validates the field value
12962 * @return {Boolean} True if the value is valid, else false
12964 validate : function(){
12965 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12966 if(this.disabled || this.validateValue(this.getRawValue())){
12971 this.markInvalid();
12977 * Validates a value according to the field's validation rules and marks the field as invalid
12978 * if the validation fails
12979 * @param {Mixed} value The value to validate
12980 * @return {Boolean} True if the value is valid, else false
12982 validateValue : function(value)
12984 if(this.getVisibilityEl().hasClass('hidden')){
12988 if(value.length < 1) { // if it's blank
12989 if(this.allowBlank){
12995 if(value.length < this.minLength){
12998 if(value.length > this.maxLength){
13002 var vt = Roo.form.VTypes;
13003 if(!vt[this.vtype](value, this)){
13007 if(typeof this.validator == "function"){
13008 var msg = this.validator(value);
13009 if (typeof(msg) == 'string') {
13010 this.invalidText = msg;
13017 if(this.regex && !this.regex.test(value)){
13025 fireKey : function(e){
13026 //Roo.log('field ' + e.getKey());
13027 if(e.isNavKeyPress()){
13028 this.fireEvent("specialkey", this, e);
13031 focus : function (selectText){
13033 this.inputEl().focus();
13034 if(selectText === true){
13035 this.inputEl().dom.select();
13041 onFocus : function(){
13042 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13043 // this.el.addClass(this.focusClass);
13045 if(!this.hasFocus){
13046 this.hasFocus = true;
13047 this.startValue = this.getValue();
13048 this.fireEvent("focus", this);
13052 beforeBlur : Roo.emptyFn,
13056 onBlur : function(){
13058 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13059 //this.el.removeClass(this.focusClass);
13061 this.hasFocus = false;
13062 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13065 var v = this.getValue();
13066 if(String(v) !== String(this.startValue)){
13067 this.fireEvent('change', this, v, this.startValue);
13069 this.fireEvent("blur", this);
13072 onChange : function(e)
13074 var v = this.getValue();
13075 if(String(v) !== String(this.startValue)){
13076 this.fireEvent('change', this, v, this.startValue);
13082 * Resets the current field value to the originally loaded value and clears any validation messages
13084 reset : function(){
13085 this.setValue(this.originalValue);
13086 // this.validate();
13087 this.el.removeClass([this.invalidClass, this.validClass]);
13088 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13091 * Returns the name of the field
13092 * @return {Mixed} name The name field
13094 getName: function(){
13098 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
13099 * @return {Mixed} value The field value
13101 getValue : function(){
13102 var v = this.inputEl().getValue();
13106 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
13107 * @return {Mixed} value The field value
13109 getRawValue : function(){
13110 var v = this.inputEl().getValue();
13116 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
13117 * @param {Mixed} value The value to set
13119 setRawValue : function(v){
13120 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13123 selectText : function(start, end){
13124 var v = this.getRawValue();
13126 start = start === undefined ? 0 : start;
13127 end = end === undefined ? v.length : end;
13128 var d = this.inputEl().dom;
13129 if(d.setSelectionRange){
13130 d.setSelectionRange(start, end);
13131 }else if(d.createTextRange){
13132 var range = d.createTextRange();
13133 range.moveStart("character", start);
13134 range.moveEnd("character", v.length-end);
13141 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
13142 * @param {Mixed} value The value to set
13144 setValue : function(v){
13147 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13153 processValue : function(value){
13154 if(this.stripCharsRe){
13155 var newValue = value.replace(this.stripCharsRe, '');
13156 if(newValue !== value){
13157 this.setRawValue(newValue);
13164 preFocus : function(){
13166 if(this.selectOnFocus){
13167 this.inputEl().dom.select();
13170 filterKeys : function(e){
13171 var k = e.getKey();
13172 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13175 var c = e.getCharCode(), cc = String.fromCharCode(c);
13176 if(Roo.isIE && (e.isSpecialKey() || !cc)){
13179 if(!this.maskRe.test(cc)){
13184 * Clear any invalid styles/messages for this field
13186 clearInvalid : function(){
13188 if(!this.el || this.preventMark){ // not rendered
13193 this.el.removeClass([this.invalidClass, 'is-invalid']);
13195 if(this.hasFeedback && this.inputType != 'hidden'){
13197 var feedback = this.el.select('.form-control-feedback', true).first();
13200 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13202 feedback.update('');
13208 if(this.indicator){
13209 this.indicator.removeClass('visible');
13210 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13213 this.fireEvent('valid', this);
13217 * Mark this field as valid
13219 markValid : function()
13221 if(!this.el || this.preventMark){ // not rendered...
13225 this.el.removeClass([this.invalidClass, this.validClass]);
13226 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13228 var feedback = this.el.select('.form-control-feedback', true).first();
13231 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13232 feedback.update('');
13236 if(this.indicator){
13237 this.indicator.removeClass('visible');
13238 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13246 if(this.allowBlank && !this.getRawValue().length){
13249 if (Roo.bootstrap.version == 3) {
13250 this.el.addClass(this.validClass);
13252 this.inputEl().addClass('is-valid');
13255 if(this.hasFeedback && this.inputType != 'hidden'){
13257 var feedback = this.el.select('.form-control-feedback', true).first();
13260 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13261 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13266 this.fireEvent('valid', this);
13270 * Mark this field as invalid
13271 * @param {String} msg The validation message
13273 markInvalid : function(msg)
13275 if(!this.el || this.preventMark){ // not rendered
13279 this.el.removeClass([this.invalidClass, this.validClass]);
13280 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13282 var feedback = this.el.select('.form-control-feedback', true).first();
13285 this.el.select('.form-control-feedback', true).first().removeClass(
13286 [this.invalidFeedbackClass, this.validFeedbackClass]);
13287 feedback.update('');
13295 if(this.allowBlank && !this.getRawValue().length){
13299 if(this.indicator){
13300 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13301 this.indicator.addClass('visible');
13303 if (Roo.bootstrap.version == 3) {
13304 this.el.addClass(this.invalidClass);
13306 this.inputEl().addClass('is-invalid');
13311 if(this.hasFeedback && this.inputType != 'hidden'){
13313 var feedback = this.el.select('.form-control-feedback', true).first();
13316 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13318 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13320 feedback.update(this.invalidText);
13322 if(!this.allowBlank && !this.getRawValue().length){
13323 feedback.update(this.blankText);
13332 this.fireEvent('invalid', this, msg);
13335 SafariOnKeyDown : function(event)
13337 // this is a workaround for a password hang bug on chrome/ webkit.
13338 if (this.inputEl().dom.type != 'password') {
13342 var isSelectAll = false;
13344 if(this.inputEl().dom.selectionEnd > 0){
13345 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13347 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13348 event.preventDefault();
13353 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13355 event.preventDefault();
13356 // this is very hacky as keydown always get's upper case.
13358 var cc = String.fromCharCode(event.getCharCode());
13359 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
13363 adjustWidth : function(tag, w){
13364 tag = tag.toLowerCase();
13365 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13366 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13367 if(tag == 'input'){
13370 if(tag == 'textarea'){
13373 }else if(Roo.isOpera){
13374 if(tag == 'input'){
13377 if(tag == 'textarea'){
13385 setFieldLabel : function(v)
13387 if(!this.rendered){
13391 if(this.indicatorEl()){
13392 var ar = this.el.select('label > span',true);
13394 if (ar.elements.length) {
13395 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13396 this.fieldLabel = v;
13400 var br = this.el.select('label',true);
13402 if(br.elements.length) {
13403 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13404 this.fieldLabel = v;
13408 Roo.log('Cannot Found any of label > span || label in input');
13412 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13413 this.fieldLabel = v;
13428 * @class Roo.bootstrap.form.TextArea
13429 * @extends Roo.bootstrap.form.Input
13430 * Bootstrap TextArea class
13431 * @cfg {Number} cols Specifies the visible width of a text area
13432 * @cfg {Number} rows Specifies the visible number of lines in a text area
13433 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13434 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13435 * @cfg {string} html text
13438 * Create a new TextArea
13439 * @param {Object} config The config object
13442 Roo.bootstrap.form.TextArea = function(config){
13443 Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13447 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input, {
13457 getAutoCreate : function(){
13459 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13465 if(this.inputType != 'hidden'){
13466 cfg.cls = 'form-group' //input-group
13474 value : this.value || '',
13475 html: this.html || '',
13476 cls : 'form-control',
13477 placeholder : this.placeholder || ''
13481 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13482 input.maxLength = this.maxLength;
13486 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13490 input.cols = this.cols;
13493 if (this.readOnly) {
13494 input.readonly = true;
13498 input.name = this.name;
13502 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13506 ['xs','sm','md','lg'].map(function(size){
13507 if (settings[size]) {
13508 cfg.cls += ' col-' + size + '-' + settings[size];
13512 var inputblock = input;
13514 if(this.hasFeedback){
13518 cls: 'glyphicon form-control-feedback'
13522 cls : 'has-feedback',
13531 if (this.before || this.after) {
13534 cls : 'input-group',
13538 inputblock.cn.push({
13540 cls : 'input-group-addon',
13545 inputblock.cn.push(input);
13547 if(this.hasFeedback){
13548 inputblock.cls += ' has-feedback';
13549 inputblock.cn.push(feedback);
13553 inputblock.cn.push({
13555 cls : 'input-group-addon',
13563 cfg = this.getAutoCreateLabel( cfg, inputblock );
13567 if (this.disabled) {
13568 input.disabled=true;
13575 * return the real textarea element.
13577 inputEl: function ()
13579 return this.el.select('textarea.form-control',true).first();
13583 * Clear any invalid styles/messages for this field
13585 clearInvalid : function()
13588 if(!this.el || this.preventMark){ // not rendered
13592 var label = this.el.select('label', true).first();
13593 //var icon = this.el.select('i.fa-star', true).first();
13595 //if(label && icon){
13598 this.el.removeClass( this.validClass);
13599 this.inputEl().removeClass('is-invalid');
13601 if(this.hasFeedback && this.inputType != 'hidden'){
13603 var feedback = this.el.select('.form-control-feedback', true).first();
13606 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13608 feedback.update('');
13613 this.fireEvent('valid', this);
13617 * Mark this field as valid
13619 markValid : function()
13621 if(!this.el || this.preventMark){ // not rendered
13625 this.el.removeClass([this.invalidClass, this.validClass]);
13626 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13628 var feedback = this.el.select('.form-control-feedback', true).first();
13631 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13632 feedback.update('');
13635 if(this.disabled || this.allowBlank){
13639 var label = this.el.select('label', true).first();
13640 var icon = this.el.select('i.fa-star', true).first();
13642 //if(label && icon){
13645 if (Roo.bootstrap.version == 3) {
13646 this.el.addClass(this.validClass);
13648 this.inputEl().addClass('is-valid');
13652 if(this.hasFeedback && this.inputType != 'hidden'){
13654 var feedback = this.el.select('.form-control-feedback', true).first();
13657 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13658 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13663 this.fireEvent('valid', this);
13667 * Mark this field as invalid
13668 * @param {String} msg The validation message
13670 markInvalid : function(msg)
13672 if(!this.el || this.preventMark){ // not rendered
13676 this.el.removeClass([this.invalidClass, this.validClass]);
13677 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13679 var feedback = this.el.select('.form-control-feedback', true).first();
13682 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13683 feedback.update('');
13690 var label = this.el.select('label', true).first();
13691 //var icon = this.el.select('i.fa-star', true).first();
13693 //if(!this.getValue().length && label && !icon){
13694 /* this.el.createChild({
13696 cls : 'text-danger fa fa-lg fa-star',
13697 tooltip : 'This field is required',
13698 style : 'margin-right:5px;'
13703 if (Roo.bootstrap.version == 3) {
13704 this.el.addClass(this.invalidClass);
13706 this.inputEl().addClass('is-invalid');
13709 // fixme ... this may be depricated need to test..
13710 if(this.hasFeedback && this.inputType != 'hidden'){
13712 var feedback = this.el.select('.form-control-feedback', true).first();
13715 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13717 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13719 feedback.update(this.invalidText);
13721 if(!this.allowBlank && !this.getRawValue().length){
13722 feedback.update(this.blankText);
13729 this.fireEvent('invalid', this, msg);
13737 * trigger field - base class for combo..
13742 * @class Roo.bootstrap.form.TriggerField
13743 * @extends Roo.bootstrap.form.Input
13744 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13745 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13746 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13747 * for which you can provide a custom implementation. For example:
13749 var trigger = new Roo.bootstrap.form.TriggerField();
13750 trigger.onTriggerClick = myTriggerFn;
13751 trigger.applyTo('my-field');
13754 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13755 * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13756 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13757 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13758 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13761 * Create a new TriggerField.
13762 * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13763 * to the base TextField)
13765 Roo.bootstrap.form.TriggerField = function(config){
13766 this.mimicing = false;
13767 Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13770 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input, {
13772 * @cfg {String} triggerClass A CSS class to apply to the trigger
13775 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13780 * @cfg {Boolean} removable (true|false) special filter default false
13784 /** @cfg {Boolean} grow @hide */
13785 /** @cfg {Number} growMin @hide */
13786 /** @cfg {Number} growMax @hide */
13792 autoSize: Roo.emptyFn,
13796 deferHeight : true,
13799 actionMode : 'wrap',
13804 getAutoCreate : function(){
13806 var align = this.labelAlign || this.parentLabelAlign();
13811 cls: 'form-group' //input-group
13818 type : this.inputType,
13819 cls : 'form-control',
13820 autocomplete: 'new-password',
13821 placeholder : this.placeholder || ''
13825 input.name = this.name;
13828 input.cls += ' input-' + this.size;
13831 if (this.disabled) {
13832 input.disabled=true;
13835 var inputblock = input;
13837 if(this.hasFeedback && !this.allowBlank){
13841 cls: 'glyphicon form-control-feedback'
13844 if(this.removable && !this.editable ){
13846 cls : 'has-feedback',
13852 cls : 'roo-combo-removable-btn close'
13859 cls : 'has-feedback',
13868 if(this.removable && !this.editable ){
13870 cls : 'roo-removable',
13876 cls : 'roo-combo-removable-btn close'
13883 if (this.before || this.after) {
13886 cls : 'input-group',
13890 inputblock.cn.push({
13892 cls : 'input-group-addon input-group-prepend input-group-text',
13897 inputblock.cn.push(input);
13899 if(this.hasFeedback && !this.allowBlank){
13900 inputblock.cls += ' has-feedback';
13901 inputblock.cn.push(feedback);
13905 inputblock.cn.push({
13907 cls : 'input-group-addon input-group-append input-group-text',
13916 var ibwrap = inputblock;
13921 cls: 'roo-select2-choices',
13925 cls: 'roo-select2-search-field',
13937 cls: 'roo-select2-container input-group',
13942 cls: 'form-hidden-field'
13948 if(!this.multiple && this.showToggleBtn){
13954 if (this.caret != false) {
13957 cls: 'fa fa-' + this.caret
13964 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13966 Roo.bootstrap.version == 3 ? caret : '',
13969 cls: 'combobox-clear',
13983 combobox.cls += ' roo-select2-container-multi';
13987 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13988 tooltip : 'This field is required'
13991 if (this.allowBlank) {
13994 style : 'display:none'
14000 if (align ==='left' && this.fieldLabel.length) {
14002 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
14009 cls : 'control-label',
14010 html : this.fieldLabel
14022 var labelCfg = cfg.cn[1];
14023 var contentCfg = cfg.cn[2];
14025 if(this.indicatorpos == 'right'){
14030 cls : 'control-label',
14034 html : this.fieldLabel
14048 labelCfg = cfg.cn[0];
14049 contentCfg = cfg.cn[1];
14052 if(this.labelWidth > 12){
14053 labelCfg.style = "width: " + this.labelWidth + 'px';
14056 if(this.labelWidth < 13 && this.labelmd == 0){
14057 this.labelmd = this.labelWidth;
14060 if(this.labellg > 0){
14061 labelCfg.cls += ' col-lg-' + this.labellg;
14062 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14065 if(this.labelmd > 0){
14066 labelCfg.cls += ' col-md-' + this.labelmd;
14067 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14070 if(this.labelsm > 0){
14071 labelCfg.cls += ' col-sm-' + this.labelsm;
14072 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14075 if(this.labelxs > 0){
14076 labelCfg.cls += ' col-xs-' + this.labelxs;
14077 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14080 } else if ( this.fieldLabel.length) {
14081 // Roo.log(" label");
14086 //cls : 'input-group-addon',
14087 html : this.fieldLabel
14095 if(this.indicatorpos == 'right'){
14103 html : this.fieldLabel
14117 // Roo.log(" no label && no align");
14124 ['xs','sm','md','lg'].map(function(size){
14125 if (settings[size]) {
14126 cfg.cls += ' col-' + size + '-' + settings[size];
14137 onResize : function(w, h){
14138 // Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14139 // if(typeof w == 'number'){
14140 // var x = w - this.trigger.getWidth();
14141 // this.inputEl().setWidth(this.adjustWidth('input', x));
14142 // this.trigger.setStyle('left', x+'px');
14147 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14150 getResizeEl : function(){
14151 return this.inputEl();
14155 getPositionEl : function(){
14156 return this.inputEl();
14160 alignErrorIcon : function(){
14161 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14165 initEvents : function(){
14169 Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14170 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14171 if(!this.multiple && this.showToggleBtn){
14172 this.trigger = this.el.select('span.dropdown-toggle',true).first();
14173 if(this.hideTrigger){
14174 this.trigger.setDisplayed(false);
14176 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14180 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14183 if(this.removable && !this.editable && !this.tickable){
14184 var close = this.closeTriggerEl();
14187 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14188 close.on('click', this.removeBtnClick, this, close);
14192 //this.trigger.addClassOnOver('x-form-trigger-over');
14193 //this.trigger.addClassOnClick('x-form-trigger-click');
14196 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14200 closeTriggerEl : function()
14202 var close = this.el.select('.roo-combo-removable-btn', true).first();
14203 return close ? close : false;
14206 removeBtnClick : function(e, h, el)
14208 e.preventDefault();
14210 if(this.fireEvent("remove", this) !== false){
14212 this.fireEvent("afterremove", this)
14216 createList : function()
14218 this.list = Roo.get(document.body).createChild({
14219 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14220 cls: 'typeahead typeahead-long dropdown-menu shadow',
14221 style: 'display:none'
14224 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14229 initTrigger : function(){
14234 onDestroy : function(){
14236 this.trigger.removeAllListeners();
14237 // this.trigger.remove();
14240 // this.wrap.remove();
14242 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14246 onFocus : function(){
14247 Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14249 if(!this.mimicing){
14250 this.wrap.addClass('x-trigger-wrap-focus');
14251 this.mimicing = true;
14252 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14253 if(this.monitorTab){
14254 this.el.on("keydown", this.checkTab, this);
14261 checkTab : function(e){
14262 if(e.getKey() == e.TAB){
14263 this.triggerBlur();
14268 onBlur : function(){
14273 mimicBlur : function(e, t){
14275 if(!this.wrap.contains(t) && this.validateBlur()){
14276 this.triggerBlur();
14282 triggerBlur : function(){
14283 this.mimicing = false;
14284 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14285 if(this.monitorTab){
14286 this.el.un("keydown", this.checkTab, this);
14288 //this.wrap.removeClass('x-trigger-wrap-focus');
14289 Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14293 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14294 validateBlur : function(e, t){
14299 onDisable : function(){
14300 this.inputEl().dom.disabled = true;
14301 //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14303 // this.wrap.addClass('x-item-disabled');
14308 onEnable : function(){
14309 this.inputEl().dom.disabled = false;
14310 //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14312 // this.el.removeClass('x-item-disabled');
14317 onShow : function(){
14318 var ae = this.getActionEl();
14321 ae.dom.style.display = '';
14322 ae.dom.style.visibility = 'visible';
14328 onHide : function(){
14329 var ae = this.getActionEl();
14330 ae.dom.style.display = 'none';
14334 * The function that should handle the trigger's click event. This method does nothing by default until overridden
14335 * by an implementing function.
14337 * @param {EventObject} e
14339 onTriggerClick : Roo.emptyFn
14347 * @class Roo.bootstrap.form.CardUploader
14348 * @extends Roo.bootstrap.Button
14349 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14350 * @cfg {Number} errorTimeout default 3000
14351 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
14352 * @cfg {Array} html The button text.
14356 * Create a new CardUploader
14357 * @param {Object} config The config object
14360 Roo.bootstrap.form.CardUploader = function(config){
14364 Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14367 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
14375 * When a image is clicked on - and needs to display a slideshow or similar..
14376 * @param {Roo.bootstrap.Card} this
14377 * @param {Object} The image information data
14383 * When a the download link is clicked
14384 * @param {Roo.bootstrap.Card} this
14385 * @param {Object} The image information data contains
14392 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input, {
14395 errorTimeout : 3000,
14399 fileCollection : false,
14402 getAutoCreate : function()
14406 cls :'form-group' ,
14411 //cls : 'input-group-addon',
14412 html : this.fieldLabel
14420 value : this.value,
14421 cls : 'd-none form-control'
14426 multiple : 'multiple',
14428 cls : 'd-none roo-card-upload-selector'
14432 cls : 'roo-card-uploader-button-container w-100 mb-2'
14435 cls : 'card-columns roo-card-uploader-container'
14445 getChildContainer : function() /// what children are added to.
14447 return this.containerEl;
14450 getButtonContainer : function() /// what children are added to.
14452 return this.el.select(".roo-card-uploader-button-container").first();
14455 initEvents : function()
14458 Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14462 xns: Roo.bootstrap,
14465 container_method : 'getButtonContainer' ,
14466 html : this.html, // fix changable?
14469 'click' : function(btn, e) {
14478 this.urlAPI = (window.createObjectURL && window) ||
14479 (window.URL && URL.revokeObjectURL && URL) ||
14480 (window.webkitURL && webkitURL);
14485 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14487 this.selectorEl.on('change', this.onFileSelected, this);
14490 this.images.forEach(function(img) {
14493 this.images = false;
14495 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14501 onClick : function(e)
14503 e.preventDefault();
14505 this.selectorEl.dom.click();
14509 onFileSelected : function(e)
14511 e.preventDefault();
14513 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14517 Roo.each(this.selectorEl.dom.files, function(file){
14518 this.addFile(file);
14527 addFile : function(file)
14530 if(typeof(file) === 'string'){
14531 throw "Add file by name?"; // should not happen
14535 if(!file || !this.urlAPI){
14545 var url = _this.urlAPI.createObjectURL( file);
14548 id : Roo.bootstrap.form.CardUploader.ID--,
14549 is_uploaded : false,
14553 mimetype : file.type,
14561 * addCard - add an Attachment to the uploader
14562 * @param data - the data about the image to upload
14566 title : "Title of file",
14567 is_uploaded : false,
14568 src : "http://.....",
14569 srcfile : { the File upload object },
14570 mimetype : file.type,
14573 .. any other data...
14579 addCard : function (data)
14581 // hidden input element?
14582 // if the file is not an image...
14583 //then we need to use something other that and header_image
14588 xns : Roo.bootstrap,
14589 xtype : 'CardFooter',
14592 xns : Roo.bootstrap,
14598 xns : Roo.bootstrap,
14600 html : String.format("<small>{0}</small>", data.title),
14601 cls : 'col-10 text-left',
14606 click : function() {
14608 t.fireEvent( "download", t, data );
14614 xns : Roo.bootstrap,
14616 style: 'max-height: 28px; ',
14622 click : function() {
14623 t.removeCard(data.id)
14635 var cn = this.addxtype(
14638 xns : Roo.bootstrap,
14641 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14642 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14643 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14648 initEvents : function() {
14649 Roo.bootstrap.Card.prototype.initEvents.call(this);
14651 this.imgEl = this.el.select('.card-img-top').first();
14653 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14654 this.imgEl.set({ 'pointer' : 'cursor' });
14657 this.getCardFooter().addClass('p-1');
14664 // dont' really need ot update items.
14665 // this.items.push(cn);
14666 this.fileCollection.add(cn);
14668 if (!data.srcfile) {
14669 this.updateInput();
14674 var reader = new FileReader();
14675 reader.addEventListener("load", function() {
14676 data.srcdata = reader.result;
14679 reader.readAsDataURL(data.srcfile);
14684 removeCard : function(id)
14687 var card = this.fileCollection.get(id);
14688 card.data.is_deleted = 1;
14689 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14690 //this.fileCollection.remove(card);
14691 //this.items = this.items.filter(function(e) { return e != card });
14692 // dont' really need ot update items.
14693 card.el.dom.parentNode.removeChild(card.el.dom);
14694 this.updateInput();
14700 this.fileCollection.each(function(card) {
14701 if (card.el.dom && card.el.dom.parentNode) {
14702 card.el.dom.parentNode.removeChild(card.el.dom);
14705 this.fileCollection.clear();
14706 this.updateInput();
14709 updateInput : function()
14712 this.fileCollection.each(function(e) {
14716 this.inputEl().dom.value = JSON.stringify(data);
14726 Roo.bootstrap.form.CardUploader.ID = -1;/*
14728 * Ext JS Library 1.1.1
14729 * Copyright(c) 2006-2007, Ext JS, LLC.
14731 * Originally Released Under LGPL - original licence link has changed is not relivant.
14734 * <script type="text/javascript">
14739 * @class Roo.data.SortTypes
14741 * Defines the default sorting (casting?) comparison functions used when sorting data.
14743 Roo.data.SortTypes = {
14745 * Default sort that does nothing
14746 * @param {Mixed} s The value being converted
14747 * @return {Mixed} The comparison value
14749 none : function(s){
14754 * The regular expression used to strip tags
14758 stripTagsRE : /<\/?[^>]+>/gi,
14761 * Strips all HTML tags to sort on text only
14762 * @param {Mixed} s The value being converted
14763 * @return {String} The comparison value
14765 asText : function(s){
14766 return String(s).replace(this.stripTagsRE, "");
14770 * Strips all HTML tags to sort on text only - Case insensitive
14771 * @param {Mixed} s The value being converted
14772 * @return {String} The comparison value
14774 asUCText : function(s){
14775 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14779 * Case insensitive string
14780 * @param {Mixed} s The value being converted
14781 * @return {String} The comparison value
14783 asUCString : function(s) {
14784 return String(s).toUpperCase();
14789 * @param {Mixed} s The value being converted
14790 * @return {Number} The comparison value
14792 asDate : function(s) {
14796 if(s instanceof Date){
14797 return s.getTime();
14799 return Date.parse(String(s));
14804 * @param {Mixed} s The value being converted
14805 * @return {Float} The comparison value
14807 asFloat : function(s) {
14808 var val = parseFloat(String(s).replace(/,/g, ""));
14817 * @param {Mixed} s The value being converted
14818 * @return {Number} The comparison value
14820 asInt : function(s) {
14821 var val = parseInt(String(s).replace(/,/g, ""));
14829 * Ext JS Library 1.1.1
14830 * Copyright(c) 2006-2007, Ext JS, LLC.
14832 * Originally Released Under LGPL - original licence link has changed is not relivant.
14835 * <script type="text/javascript">
14839 * @class Roo.data.Record
14840 * Instances of this class encapsulate both record <em>definition</em> information, and record
14841 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14842 * to access Records cached in an {@link Roo.data.Store} object.<br>
14844 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14845 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14848 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14850 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14851 * {@link #create}. The parameters are the same.
14852 * @param {Array} data An associative Array of data values keyed by the field name.
14853 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14854 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14855 * not specified an integer id is generated.
14857 Roo.data.Record = function(data, id){
14858 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14863 * Generate a constructor for a specific record layout.
14864 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14865 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14866 * Each field definition object may contain the following properties: <ul>
14867 * <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,
14868 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14869 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14870 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14871 * is being used, then this is a string containing the javascript expression to reference the data relative to
14872 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14873 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14874 * this may be omitted.</p></li>
14875 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14876 * <ul><li>auto (Default, implies no conversion)</li>
14881 * <li>date</li></ul></p></li>
14882 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14883 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14884 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14885 * by the Reader into an object that will be stored in the Record. It is passed the
14886 * following parameters:<ul>
14887 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14889 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14891 * <br>usage:<br><pre><code>
14892 var TopicRecord = Roo.data.Record.create(
14893 {name: 'title', mapping: 'topic_title'},
14894 {name: 'author', mapping: 'username'},
14895 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14896 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14897 {name: 'lastPoster', mapping: 'user2'},
14898 {name: 'excerpt', mapping: 'post_text'}
14901 var myNewRecord = new TopicRecord({
14902 title: 'Do my job please',
14905 lastPost: new Date(),
14906 lastPoster: 'Animal',
14907 excerpt: 'No way dude!'
14909 myStore.add(myNewRecord);
14914 Roo.data.Record.create = function(o){
14915 var f = function(){
14916 f.superclass.constructor.apply(this, arguments);
14918 Roo.extend(f, Roo.data.Record);
14919 var p = f.prototype;
14920 p.fields = new Roo.util.MixedCollection(false, function(field){
14923 for(var i = 0, len = o.length; i < len; i++){
14924 p.fields.add(new Roo.data.Field(o[i]));
14926 f.getField = function(name){
14927 return p.fields.get(name);
14932 Roo.data.Record.AUTO_ID = 1000;
14933 Roo.data.Record.EDIT = 'edit';
14934 Roo.data.Record.REJECT = 'reject';
14935 Roo.data.Record.COMMIT = 'commit';
14937 Roo.data.Record.prototype = {
14939 * Readonly flag - true if this record has been modified.
14948 join : function(store){
14949 this.store = store;
14953 * Set the named field to the specified value.
14954 * @param {String} name The name of the field to set.
14955 * @param {Object} value The value to set the field to.
14957 set : function(name, value){
14958 if(this.data[name] == value){
14962 if(!this.modified){
14963 this.modified = {};
14965 if(typeof this.modified[name] == 'undefined'){
14966 this.modified[name] = this.data[name];
14968 this.data[name] = value;
14969 if(!this.editing && this.store){
14970 this.store.afterEdit(this);
14975 * Get the value of the named field.
14976 * @param {String} name The name of the field to get the value of.
14977 * @return {Object} The value of the field.
14979 get : function(name){
14980 return this.data[name];
14984 beginEdit : function(){
14985 this.editing = true;
14986 this.modified = {};
14990 cancelEdit : function(){
14991 this.editing = false;
14992 delete this.modified;
14996 endEdit : function(){
14997 this.editing = false;
14998 if(this.dirty && this.store){
14999 this.store.afterEdit(this);
15004 * Usually called by the {@link Roo.data.Store} which owns the Record.
15005 * Rejects all changes made to the Record since either creation, or the last commit operation.
15006 * Modified fields are reverted to their original values.
15008 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15009 * of reject operations.
15011 reject : function(){
15012 var m = this.modified;
15014 if(typeof m[n] != "function"){
15015 this.data[n] = m[n];
15018 this.dirty = false;
15019 delete this.modified;
15020 this.editing = false;
15022 this.store.afterReject(this);
15027 * Usually called by the {@link Roo.data.Store} which owns the Record.
15028 * Commits all changes made to the Record since either creation, or the last commit operation.
15030 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15031 * of commit operations.
15033 commit : function(){
15034 this.dirty = false;
15035 delete this.modified;
15036 this.editing = false;
15038 this.store.afterCommit(this);
15043 hasError : function(){
15044 return this.error != null;
15048 clearError : function(){
15053 * Creates a copy of this record.
15054 * @param {String} id (optional) A new record id if you don't want to use this record's id
15057 copy : function(newId) {
15058 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15062 * Ext JS Library 1.1.1
15063 * Copyright(c) 2006-2007, Ext JS, LLC.
15065 * Originally Released Under LGPL - original licence link has changed is not relivant.
15068 * <script type="text/javascript">
15074 * @class Roo.data.Store
15075 * @extends Roo.util.Observable
15076 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15077 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15079 * 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
15080 * has no knowledge of the format of the data returned by the Proxy.<br>
15082 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15083 * instances from the data object. These records are cached and made available through accessor functions.
15085 * Creates a new Store.
15086 * @param {Object} config A config object containing the objects needed for the Store to access data,
15087 * and read the data into Records.
15089 Roo.data.Store = function(config){
15090 this.data = new Roo.util.MixedCollection(false);
15091 this.data.getKey = function(o){
15094 this.baseParams = {};
15096 this.paramNames = {
15101 "multisort" : "_multisort"
15104 if(config && config.data){
15105 this.inlineData = config.data;
15106 delete config.data;
15109 Roo.apply(this, config);
15111 if(this.reader){ // reader passed
15112 this.reader = Roo.factory(this.reader, Roo.data);
15113 this.reader.xmodule = this.xmodule || false;
15114 if(!this.recordType){
15115 this.recordType = this.reader.recordType;
15117 if(this.reader.onMetaChange){
15118 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15122 if(this.recordType){
15123 this.fields = this.recordType.prototype.fields;
15125 this.modified = [];
15129 * @event datachanged
15130 * Fires when the data cache has changed, and a widget which is using this Store
15131 * as a Record cache should refresh its view.
15132 * @param {Store} this
15134 datachanged : true,
15136 * @event metachange
15137 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15138 * @param {Store} this
15139 * @param {Object} meta The JSON metadata
15144 * Fires when Records have been added to the Store
15145 * @param {Store} this
15146 * @param {Roo.data.Record[]} records The array of Records added
15147 * @param {Number} index The index at which the record(s) were added
15152 * Fires when a Record has been removed from the Store
15153 * @param {Store} this
15154 * @param {Roo.data.Record} record The Record that was removed
15155 * @param {Number} index The index at which the record was removed
15160 * Fires when a Record has been updated
15161 * @param {Store} this
15162 * @param {Roo.data.Record} record The Record that was updated
15163 * @param {String} operation The update operation being performed. Value may be one of:
15165 Roo.data.Record.EDIT
15166 Roo.data.Record.REJECT
15167 Roo.data.Record.COMMIT
15173 * Fires when the data cache has been cleared.
15174 * @param {Store} this
15178 * @event beforeload
15179 * Fires before a request is made for a new data object. If the beforeload handler returns false
15180 * the load action will be canceled.
15181 * @param {Store} this
15182 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15186 * @event beforeloadadd
15187 * Fires after a new set of Records has been loaded.
15188 * @param {Store} this
15189 * @param {Roo.data.Record[]} records The Records that were loaded
15190 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15192 beforeloadadd : true,
15195 * Fires after a new set of Records has been loaded, before they are added to the store.
15196 * @param {Store} this
15197 * @param {Roo.data.Record[]} records The Records that were loaded
15198 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15199 * @params {Object} return from reader
15203 * @event loadexception
15204 * Fires if an exception occurs in the Proxy during loading.
15205 * Called with the signature of the Proxy's "loadexception" event.
15206 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15209 * @param {Object} ret return data from JsonData.reader() - success, totalRecords, records
15210 * @param {Object} opts - load Options
15211 * @param {Object} jsonData from your request (normally this contains the Exception)
15213 loadexception : true
15217 this.proxy = Roo.factory(this.proxy, Roo.data);
15218 this.proxy.xmodule = this.xmodule || false;
15219 this.relayEvents(this.proxy, ["loadexception"]);
15221 this.sortToggle = {};
15222 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15224 Roo.data.Store.superclass.constructor.call(this);
15226 if(this.inlineData){
15227 this.loadData(this.inlineData);
15228 delete this.inlineData;
15232 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15234 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
15235 * without a remote query - used by combo/forms at present.
15239 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15242 * @cfg {Array} data Inline data to be loaded when the store is initialized.
15245 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
15246 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15249 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15250 * on any HTTP request
15253 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15256 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15260 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15261 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15263 remoteSort : false,
15266 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15267 * loaded or when a record is removed. (defaults to false).
15269 pruneModifiedRecords : false,
15272 lastOptions : null,
15275 * Add Records to the Store and fires the add event.
15276 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15278 add : function(records){
15279 records = [].concat(records);
15280 for(var i = 0, len = records.length; i < len; i++){
15281 records[i].join(this);
15283 var index = this.data.length;
15284 this.data.addAll(records);
15285 this.fireEvent("add", this, records, index);
15289 * Remove a Record from the Store and fires the remove event.
15290 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15292 remove : function(record){
15293 var index = this.data.indexOf(record);
15294 this.data.removeAt(index);
15296 if(this.pruneModifiedRecords){
15297 this.modified.remove(record);
15299 this.fireEvent("remove", this, record, index);
15303 * Remove all Records from the Store and fires the clear event.
15305 removeAll : function(){
15307 if(this.pruneModifiedRecords){
15308 this.modified = [];
15310 this.fireEvent("clear", this);
15314 * Inserts Records to the Store at the given index and fires the add event.
15315 * @param {Number} index The start index at which to insert the passed Records.
15316 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15318 insert : function(index, records){
15319 records = [].concat(records);
15320 for(var i = 0, len = records.length; i < len; i++){
15321 this.data.insert(index, records[i]);
15322 records[i].join(this);
15324 this.fireEvent("add", this, records, index);
15328 * Get the index within the cache of the passed Record.
15329 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15330 * @return {Number} The index of the passed Record. Returns -1 if not found.
15332 indexOf : function(record){
15333 return this.data.indexOf(record);
15337 * Get the index within the cache of the Record with the passed id.
15338 * @param {String} id The id of the Record to find.
15339 * @return {Number} The index of the Record. Returns -1 if not found.
15341 indexOfId : function(id){
15342 return this.data.indexOfKey(id);
15346 * Get the Record with the specified id.
15347 * @param {String} id The id of the Record to find.
15348 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15350 getById : function(id){
15351 return this.data.key(id);
15355 * Get the Record at the specified index.
15356 * @param {Number} index The index of the Record to find.
15357 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15359 getAt : function(index){
15360 return this.data.itemAt(index);
15364 * Returns a range of Records between specified indices.
15365 * @param {Number} startIndex (optional) The starting index (defaults to 0)
15366 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15367 * @return {Roo.data.Record[]} An array of Records
15369 getRange : function(start, end){
15370 return this.data.getRange(start, end);
15374 storeOptions : function(o){
15375 o = Roo.apply({}, o);
15378 this.lastOptions = o;
15382 * Loads the Record cache from the configured Proxy using the configured Reader.
15384 * If using remote paging, then the first load call must specify the <em>start</em>
15385 * and <em>limit</em> properties in the options.params property to establish the initial
15386 * position within the dataset, and the number of Records to cache on each read from the Proxy.
15388 * <strong>It is important to note that for remote data sources, loading is asynchronous,
15389 * and this call will return before the new data has been loaded. Perform any post-processing
15390 * in a callback function, or in a "load" event handler.</strong>
15392 * @param {Object} options An object containing properties which control loading options:<ul>
15393 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15394 * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15397 data : data, // array of key=>value data like JsonReader
15398 total : data.length,
15404 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15405 * passed the following arguments:<ul>
15406 * <li>r : Roo.data.Record[]</li>
15407 * <li>options: Options object from the load call</li>
15408 * <li>success: Boolean success indicator</li></ul></li>
15409 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15410 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15413 load : function(options){
15414 options = options || {};
15415 if(this.fireEvent("beforeload", this, options) !== false){
15416 this.storeOptions(options);
15417 var p = Roo.apply(options.params || {}, this.baseParams);
15418 // if meta was not loaded from remote source.. try requesting it.
15419 if (!this.reader.metaFromRemote) {
15420 p._requestMeta = 1;
15422 if(this.sortInfo && this.remoteSort){
15423 var pn = this.paramNames;
15424 p[pn["sort"]] = this.sortInfo.field;
15425 p[pn["dir"]] = this.sortInfo.direction;
15427 if (this.multiSort) {
15428 var pn = this.paramNames;
15429 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15432 this.proxy.load(p, this.reader, this.loadRecords, this, options);
15437 * Reloads the Record cache from the configured Proxy using the configured Reader and
15438 * the options from the last load operation performed.
15439 * @param {Object} options (optional) An object containing properties which may override the options
15440 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15441 * the most recently used options are reused).
15443 reload : function(options){
15444 this.load(Roo.applyIf(options||{}, this.lastOptions));
15448 // Called as a callback by the Reader during a load operation.
15449 loadRecords : function(o, options, success){
15452 if(success !== false){
15453 this.fireEvent("load", this, [], options, o);
15455 if(options.callback){
15456 options.callback.call(options.scope || this, [], options, false);
15460 // if data returned failure - throw an exception.
15461 if (o.success === false) {
15462 // show a message if no listener is registered.
15463 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15464 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15466 // loadmask wil be hooked into this..
15467 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15470 var r = o.records, t = o.totalRecords || r.length;
15472 this.fireEvent("beforeloadadd", this, r, options, o);
15474 if(!options || options.add !== true){
15475 if(this.pruneModifiedRecords){
15476 this.modified = [];
15478 for(var i = 0, len = r.length; i < len; i++){
15482 this.data = this.snapshot;
15483 delete this.snapshot;
15486 this.data.addAll(r);
15487 this.totalLength = t;
15489 this.fireEvent("datachanged", this);
15491 this.totalLength = Math.max(t, this.data.length+r.length);
15495 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15497 var e = new Roo.data.Record({});
15499 e.set(this.parent.displayField, this.parent.emptyTitle);
15500 e.set(this.parent.valueField, '');
15505 this.fireEvent("load", this, r, options, o);
15506 if(options.callback){
15507 options.callback.call(options.scope || this, r, options, true);
15513 * Loads data from a passed data block. A Reader which understands the format of the data
15514 * must have been configured in the constructor.
15515 * @param {Object} data The data block from which to read the Records. The format of the data expected
15516 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15517 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15519 loadData : function(o, append){
15520 var r = this.reader.readRecords(o);
15521 this.loadRecords(r, {add: append}, true);
15525 * using 'cn' the nested child reader read the child array into it's child stores.
15526 * @param {Object} rec The record with a 'children array
15528 loadDataFromChildren : function(rec)
15530 this.loadData(this.reader.toLoadData(rec));
15535 * Gets the number of cached records.
15537 * <em>If using paging, this may not be the total size of the dataset. If the data object
15538 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15539 * the data set size</em>
15541 getCount : function(){
15542 return this.data.length || 0;
15546 * Gets the total number of records in the dataset as returned by the server.
15548 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15549 * the dataset size</em>
15551 getTotalCount : function(){
15552 return this.totalLength || 0;
15556 * Returns the sort state of the Store as an object with two properties:
15558 field {String} The name of the field by which the Records are sorted
15559 direction {String} The sort order, "ASC" or "DESC"
15562 getSortState : function(){
15563 return this.sortInfo;
15567 applySort : function(){
15568 if(this.sortInfo && !this.remoteSort){
15569 var s = this.sortInfo, f = s.field;
15570 var st = this.fields.get(f).sortType;
15571 var fn = function(r1, r2){
15572 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15573 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15575 this.data.sort(s.direction, fn);
15576 if(this.snapshot && this.snapshot != this.data){
15577 this.snapshot.sort(s.direction, fn);
15583 * Sets the default sort column and order to be used by the next load operation.
15584 * @param {String} fieldName The name of the field to sort by.
15585 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15587 setDefaultSort : function(field, dir){
15588 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15592 * Sort the Records.
15593 * If remote sorting is used, the sort is performed on the server, and the cache is
15594 * reloaded. If local sorting is used, the cache is sorted internally.
15595 * @param {String} fieldName The name of the field to sort by.
15596 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15598 sort : function(fieldName, dir){
15599 var f = this.fields.get(fieldName);
15601 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15603 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15604 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15609 this.sortToggle[f.name] = dir;
15610 this.sortInfo = {field: f.name, direction: dir};
15611 if(!this.remoteSort){
15613 this.fireEvent("datachanged", this);
15615 this.load(this.lastOptions);
15620 * Calls the specified function for each of the Records in the cache.
15621 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15622 * Returning <em>false</em> aborts and exits the iteration.
15623 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15625 each : function(fn, scope){
15626 this.data.each(fn, scope);
15630 * Gets all records modified since the last commit. Modified records are persisted across load operations
15631 * (e.g., during paging).
15632 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15634 getModifiedRecords : function(){
15635 return this.modified;
15639 createFilterFn : function(property, value, anyMatch){
15640 if(!value.exec){ // not a regex
15641 value = String(value);
15642 if(value.length == 0){
15645 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15647 return function(r){
15648 return value.test(r.data[property]);
15653 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15654 * @param {String} property A field on your records
15655 * @param {Number} start The record index to start at (defaults to 0)
15656 * @param {Number} end The last record index to include (defaults to length - 1)
15657 * @return {Number} The sum
15659 sum : function(property, start, end){
15660 var rs = this.data.items, v = 0;
15661 start = start || 0;
15662 end = (end || end === 0) ? end : rs.length-1;
15664 for(var i = start; i <= end; i++){
15665 v += (rs[i].data[property] || 0);
15671 * Filter the records by a specified property.
15672 * @param {String} field A field on your records
15673 * @param {String/RegExp} value Either a string that the field
15674 * should start with or a RegExp to test against the field
15675 * @param {Boolean} anyMatch True to match any part not just the beginning
15677 filter : function(property, value, anyMatch){
15678 var fn = this.createFilterFn(property, value, anyMatch);
15679 return fn ? this.filterBy(fn) : this.clearFilter();
15683 * Filter by a function. The specified function will be called with each
15684 * record in this data source. If the function returns true the record is included,
15685 * otherwise it is filtered.
15686 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15687 * @param {Object} scope (optional) The scope of the function (defaults to this)
15689 filterBy : function(fn, scope){
15690 this.snapshot = this.snapshot || this.data;
15691 this.data = this.queryBy(fn, scope||this);
15692 this.fireEvent("datachanged", this);
15696 * Query the records by a specified property.
15697 * @param {String} field A field on your records
15698 * @param {String/RegExp} value Either a string that the field
15699 * should start with or a RegExp to test against the field
15700 * @param {Boolean} anyMatch True to match any part not just the beginning
15701 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15703 query : function(property, value, anyMatch){
15704 var fn = this.createFilterFn(property, value, anyMatch);
15705 return fn ? this.queryBy(fn) : this.data.clone();
15709 * Query by a function. The specified function will be called with each
15710 * record in this data source. If the function returns true the record is included
15712 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15713 * @param {Object} scope (optional) The scope of the function (defaults to this)
15714 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15716 queryBy : function(fn, scope){
15717 var data = this.snapshot || this.data;
15718 return data.filterBy(fn, scope||this);
15722 * Collects unique values for a particular dataIndex from this store.
15723 * @param {String} dataIndex The property to collect
15724 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15725 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15726 * @return {Array} An array of the unique values
15728 collect : function(dataIndex, allowNull, bypassFilter){
15729 var d = (bypassFilter === true && this.snapshot) ?
15730 this.snapshot.items : this.data.items;
15731 var v, sv, r = [], l = {};
15732 for(var i = 0, len = d.length; i < len; i++){
15733 v = d[i].data[dataIndex];
15735 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15744 * Revert to a view of the Record cache with no filtering applied.
15745 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15747 clearFilter : function(suppressEvent){
15748 if(this.snapshot && this.snapshot != this.data){
15749 this.data = this.snapshot;
15750 delete this.snapshot;
15751 if(suppressEvent !== true){
15752 this.fireEvent("datachanged", this);
15758 afterEdit : function(record){
15759 if(this.modified.indexOf(record) == -1){
15760 this.modified.push(record);
15762 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15766 afterReject : function(record){
15767 this.modified.remove(record);
15768 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15772 afterCommit : function(record){
15773 this.modified.remove(record);
15774 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15778 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15779 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15781 commitChanges : function(){
15782 var m = this.modified.slice(0);
15783 this.modified = [];
15784 for(var i = 0, len = m.length; i < len; i++){
15790 * Cancel outstanding changes on all changed records.
15792 rejectChanges : function(){
15793 var m = this.modified.slice(0);
15794 this.modified = [];
15795 for(var i = 0, len = m.length; i < len; i++){
15800 onMetaChange : function(meta, rtype, o){
15801 this.recordType = rtype;
15802 this.fields = rtype.prototype.fields;
15803 delete this.snapshot;
15804 this.sortInfo = meta.sortInfo || this.sortInfo;
15805 this.modified = [];
15806 this.fireEvent('metachange', this, this.reader.meta);
15809 moveIndex : function(data, type)
15811 var index = this.indexOf(data);
15813 var newIndex = index + type;
15817 this.insert(newIndex, data);
15822 * Ext JS Library 1.1.1
15823 * Copyright(c) 2006-2007, Ext JS, LLC.
15825 * Originally Released Under LGPL - original licence link has changed is not relivant.
15828 * <script type="text/javascript">
15832 * @class Roo.data.SimpleStore
15833 * @extends Roo.data.Store
15834 * Small helper class to make creating Stores from Array data easier.
15835 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15836 * @cfg {Array} fields An array of field definition objects, or field name strings.
15837 * @cfg {Object} an existing reader (eg. copied from another store)
15838 * @cfg {Array} data The multi-dimensional array of data
15839 * @cfg {Roo.data.DataProxy} proxy [not-required]
15840 * @cfg {Roo.data.Reader} reader [not-required]
15842 * @param {Object} config
15844 Roo.data.SimpleStore = function(config)
15846 Roo.data.SimpleStore.superclass.constructor.call(this, {
15848 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15851 Roo.data.Record.create(config.fields)
15853 proxy : new Roo.data.MemoryProxy(config.data)
15857 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15859 * Ext JS Library 1.1.1
15860 * Copyright(c) 2006-2007, Ext JS, LLC.
15862 * Originally Released Under LGPL - original licence link has changed is not relivant.
15865 * <script type="text/javascript">
15870 * @extends Roo.data.Store
15871 * @class Roo.data.JsonStore
15872 * Small helper class to make creating Stores for JSON data easier. <br/>
15874 var store = new Roo.data.JsonStore({
15875 url: 'get-images.php',
15877 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15880 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15881 * JsonReader and HttpProxy (unless inline data is provided).</b>
15882 * @cfg {Array} fields An array of field definition objects, or field name strings.
15884 * @param {Object} config
15886 Roo.data.JsonStore = function(c){
15887 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15888 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15889 reader: new Roo.data.JsonReader(c, c.fields)
15892 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15894 * Ext JS Library 1.1.1
15895 * Copyright(c) 2006-2007, Ext JS, LLC.
15897 * Originally Released Under LGPL - original licence link has changed is not relivant.
15900 * <script type="text/javascript">
15904 Roo.data.Field = function(config){
15905 if(typeof config == "string"){
15906 config = {name: config};
15908 Roo.apply(this, config);
15911 this.type = "auto";
15914 var st = Roo.data.SortTypes;
15915 // named sortTypes are supported, here we look them up
15916 if(typeof this.sortType == "string"){
15917 this.sortType = st[this.sortType];
15920 // set default sortType for strings and dates
15921 if(!this.sortType){
15924 this.sortType = st.asUCString;
15927 this.sortType = st.asDate;
15930 this.sortType = st.none;
15935 var stripRe = /[\$,%]/g;
15937 // prebuilt conversion function for this field, instead of
15938 // switching every time we're reading a value
15940 var cv, dateFormat = this.dateFormat;
15945 cv = function(v){ return v; };
15948 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15952 return v !== undefined && v !== null && v !== '' ?
15953 parseInt(String(v).replace(stripRe, ""), 10) : '';
15958 return v !== undefined && v !== null && v !== '' ?
15959 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15964 cv = function(v){ return v === true || v === "true" || v == 1; };
15971 if(v instanceof Date){
15975 if(dateFormat == "timestamp"){
15976 return new Date(v*1000);
15978 return Date.parseDate(v, dateFormat);
15980 var parsed = Date.parse(v);
15981 return parsed ? new Date(parsed) : null;
15990 Roo.data.Field.prototype = {
15998 * Ext JS Library 1.1.1
15999 * Copyright(c) 2006-2007, Ext JS, LLC.
16001 * Originally Released Under LGPL - original licence link has changed is not relivant.
16004 * <script type="text/javascript">
16007 // Base class for reading structured data from a data source. This class is intended to be
16008 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
16011 * @class Roo.data.DataReader
16013 * Base class for reading structured data from a data source. This class is intended to be
16014 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
16017 Roo.data.DataReader = function(meta, recordType){
16021 this.recordType = recordType instanceof Array ?
16022 Roo.data.Record.create(recordType) : recordType;
16025 Roo.data.DataReader.prototype = {
16028 readerType : 'Data',
16030 * Create an empty record
16031 * @param {Object} data (optional) - overlay some values
16032 * @return {Roo.data.Record} record created.
16034 newRow : function(d) {
16036 this.recordType.prototype.fields.each(function(c) {
16038 case 'int' : da[c.name] = 0; break;
16039 case 'date' : da[c.name] = new Date(); break;
16040 case 'float' : da[c.name] = 0.0; break;
16041 case 'boolean' : da[c.name] = false; break;
16042 default : da[c.name] = ""; break;
16046 return new this.recordType(Roo.apply(da, d));
16052 * Ext JS Library 1.1.1
16053 * Copyright(c) 2006-2007, Ext JS, LLC.
16055 * Originally Released Under LGPL - original licence link has changed is not relivant.
16058 * <script type="text/javascript">
16062 * @class Roo.data.DataProxy
16063 * @extends Roo.util.Observable
16065 * This class is an abstract base class for implementations which provide retrieval of
16066 * unformatted data objects.<br>
16068 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16069 * (of the appropriate type which knows how to parse the data object) to provide a block of
16070 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16072 * Custom implementations must implement the load method as described in
16073 * {@link Roo.data.HttpProxy#load}.
16075 Roo.data.DataProxy = function(){
16078 * @event beforeload
16079 * Fires before a network request is made to retrieve a data object.
16080 * @param {Object} This DataProxy object.
16081 * @param {Object} params The params parameter to the load function.
16086 * Fires before the load method's callback is called.
16087 * @param {Object} This DataProxy object.
16088 * @param {Object} o The data object.
16089 * @param {Object} arg The callback argument object passed to the load function.
16093 * @event loadexception
16094 * Fires if an Exception occurs during data retrieval.
16095 * @param {Object} This DataProxy object.
16096 * @param {Object} o The data object.
16097 * @param {Object} arg The callback argument object passed to the load function.
16098 * @param {Object} e The Exception.
16100 loadexception : true
16102 Roo.data.DataProxy.superclass.constructor.call(this);
16105 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16108 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16112 * Ext JS Library 1.1.1
16113 * Copyright(c) 2006-2007, Ext JS, LLC.
16115 * Originally Released Under LGPL - original licence link has changed is not relivant.
16118 * <script type="text/javascript">
16121 * @class Roo.data.MemoryProxy
16122 * @extends Roo.data.DataProxy
16123 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16124 * to the Reader when its load method is called.
16126 * @param {Object} config A config object containing the objects needed for the Store to access data,
16128 Roo.data.MemoryProxy = function(config){
16130 if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16131 data = config.data;
16133 Roo.data.MemoryProxy.superclass.constructor.call(this);
16137 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16140 * @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16143 * Load data from the requested source (in this case an in-memory
16144 * data object passed to the constructor), read the data object into
16145 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16146 * process that block using the passed callback.
16147 * @param {Object} params This parameter is not used by the MemoryProxy class.
16148 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16149 * object into a block of Roo.data.Records.
16150 * @param {Function} callback The function into which to pass the block of Roo.data.records.
16151 * The function must be passed <ul>
16152 * <li>The Record block object</li>
16153 * <li>The "arg" argument from the load function</li>
16154 * <li>A boolean success indicator</li>
16156 * @param {Object} scope The scope in which to call the callback
16157 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16159 load : function(params, reader, callback, scope, arg){
16160 params = params || {};
16163 result = reader.readRecords(params.data ? params.data :this.data);
16165 this.fireEvent("loadexception", this, arg, null, e);
16166 callback.call(scope, null, arg, false);
16169 callback.call(scope, result, arg, true);
16173 update : function(params, records){
16178 * Ext JS Library 1.1.1
16179 * Copyright(c) 2006-2007, Ext JS, LLC.
16181 * Originally Released Under LGPL - original licence link has changed is not relivant.
16184 * <script type="text/javascript">
16187 * @class Roo.data.HttpProxy
16188 * @extends Roo.data.DataProxy
16189 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16190 * configured to reference a certain URL.<br><br>
16192 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16193 * from which the running page was served.<br><br>
16195 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16197 * Be aware that to enable the browser to parse an XML document, the server must set
16198 * the Content-Type header in the HTTP response to "text/xml".
16200 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16201 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
16202 * will be used to make the request.
16204 Roo.data.HttpProxy = function(conn){
16205 Roo.data.HttpProxy.superclass.constructor.call(this);
16206 // is conn a conn config or a real conn?
16208 this.useAjax = !conn || !conn.events;
16212 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16213 // thse are take from connection...
16216 * @cfg {String} url The default URL to be used for requests to the server. (defaults to undefined)
16219 * @cfg {Object} extraParams An object containing properties which are used as
16220 * extra parameters to each request made by this object. (defaults to undefined)
16223 * @cfg {Object} defaultHeaders An object containing request headers which are added
16224 * to each request made by this object. (defaults to undefined)
16227 * @cfg {String} method (GET|POST) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
16230 * @cfg {Number} timeout The timeout in milliseconds to be used for requests. (defaults to 30000)
16233 * @cfg {Boolean} autoAbort Whether this request should abort any pending requests. (defaults to false)
16239 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16243 * Return the {@link Roo.data.Connection} object being used by this Proxy.
16244 * @return {Connection} The Connection object. This object may be used to subscribe to events on
16245 * a finer-grained basis than the DataProxy events.
16247 getConnection : function(){
16248 return this.useAjax ? Roo.Ajax : this.conn;
16252 * Load data from the configured {@link Roo.data.Connection}, read the data object into
16253 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16254 * process that block using the passed callback.
16255 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16256 * for the request to the remote server.
16257 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16258 * object into a block of Roo.data.Records.
16259 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16260 * The function must be passed <ul>
16261 * <li>The Record block object</li>
16262 * <li>The "arg" argument from the load function</li>
16263 * <li>A boolean success indicator</li>
16265 * @param {Object} scope The scope in which to call the callback
16266 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16268 load : function(params, reader, callback, scope, arg){
16269 if(this.fireEvent("beforeload", this, params) !== false){
16271 params : params || {},
16273 callback : callback,
16278 callback : this.loadResponse,
16282 Roo.applyIf(o, this.conn);
16283 if(this.activeRequest){
16284 Roo.Ajax.abort(this.activeRequest);
16286 this.activeRequest = Roo.Ajax.request(o);
16288 this.conn.request(o);
16291 callback.call(scope||this, null, arg, false);
16296 loadResponse : function(o, success, response){
16297 delete this.activeRequest;
16299 this.fireEvent("loadexception", this, o, response);
16300 o.request.callback.call(o.request.scope, null, o.request.arg, false);
16305 result = o.reader.read(response);
16308 o.raw = { errorMsg : response.responseText };
16309 this.fireEvent("loadexception", this, o, response, e);
16310 o.request.callback.call(o.request.scope, o, o.request.arg, false);
16314 this.fireEvent("load", this, o, o.request.arg);
16315 o.request.callback.call(o.request.scope, result, o.request.arg, true);
16319 update : function(dataSet){
16324 updateResponse : function(dataSet){
16329 * Ext JS Library 1.1.1
16330 * Copyright(c) 2006-2007, Ext JS, LLC.
16332 * Originally Released Under LGPL - original licence link has changed is not relivant.
16335 * <script type="text/javascript">
16339 * @class Roo.data.ScriptTagProxy
16340 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16341 * other than the originating domain of the running page.<br><br>
16343 * <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
16344 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16346 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16347 * source code that is used as the source inside a <script> tag.<br><br>
16349 * In order for the browser to process the returned data, the server must wrap the data object
16350 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16351 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16352 * depending on whether the callback name was passed:
16355 boolean scriptTag = false;
16356 String cb = request.getParameter("callback");
16359 response.setContentType("text/javascript");
16361 response.setContentType("application/x-json");
16363 Writer out = response.getWriter();
16365 out.write(cb + "(");
16367 out.print(dataBlock.toJsonString());
16374 * @param {Object} config A configuration object.
16376 Roo.data.ScriptTagProxy = function(config){
16377 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16378 Roo.apply(this, config);
16379 this.head = document.getElementsByTagName("head")[0];
16382 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16384 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16386 * @cfg {String} url The URL from which to request the data object.
16389 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16393 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16394 * the server the name of the callback function set up by the load call to process the returned data object.
16395 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16396 * javascript output which calls this named function passing the data object as its only parameter.
16398 callbackParam : "callback",
16400 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16401 * name to the request.
16406 * Load data from the configured URL, read the data object into
16407 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16408 * process that block using the passed callback.
16409 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16410 * for the request to the remote server.
16411 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16412 * object into a block of Roo.data.Records.
16413 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16414 * The function must be passed <ul>
16415 * <li>The Record block object</li>
16416 * <li>The "arg" argument from the load function</li>
16417 * <li>A boolean success indicator</li>
16419 * @param {Object} scope The scope in which to call the callback
16420 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16422 load : function(params, reader, callback, scope, arg){
16423 if(this.fireEvent("beforeload", this, params) !== false){
16425 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16427 var url = this.url;
16428 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16430 url += "&_dc=" + (new Date().getTime());
16432 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16435 cb : "stcCallback"+transId,
16436 scriptId : "stcScript"+transId,
16440 callback : callback,
16446 window[trans.cb] = function(o){
16447 conn.handleResponse(o, trans);
16450 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16452 if(this.autoAbort !== false){
16456 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16458 var script = document.createElement("script");
16459 script.setAttribute("src", url);
16460 script.setAttribute("type", "text/javascript");
16461 script.setAttribute("id", trans.scriptId);
16462 this.head.appendChild(script);
16464 this.trans = trans;
16466 callback.call(scope||this, null, arg, false);
16471 isLoading : function(){
16472 return this.trans ? true : false;
16476 * Abort the current server request.
16478 abort : function(){
16479 if(this.isLoading()){
16480 this.destroyTrans(this.trans);
16485 destroyTrans : function(trans, isLoaded){
16486 this.head.removeChild(document.getElementById(trans.scriptId));
16487 clearTimeout(trans.timeoutId);
16489 window[trans.cb] = undefined;
16491 delete window[trans.cb];
16494 // if hasn't been loaded, wait for load to remove it to prevent script error
16495 window[trans.cb] = function(){
16496 window[trans.cb] = undefined;
16498 delete window[trans.cb];
16505 handleResponse : function(o, trans){
16506 this.trans = false;
16507 this.destroyTrans(trans, true);
16510 result = trans.reader.readRecords(o);
16512 this.fireEvent("loadexception", this, o, trans.arg, e);
16513 trans.callback.call(trans.scope||window, null, trans.arg, false);
16516 this.fireEvent("load", this, o, trans.arg);
16517 trans.callback.call(trans.scope||window, result, trans.arg, true);
16521 handleFailure : function(trans){
16522 this.trans = false;
16523 this.destroyTrans(trans, false);
16524 this.fireEvent("loadexception", this, null, trans.arg);
16525 trans.callback.call(trans.scope||window, null, trans.arg, false);
16529 * Ext JS Library 1.1.1
16530 * Copyright(c) 2006-2007, Ext JS, LLC.
16532 * Originally Released Under LGPL - original licence link has changed is not relivant.
16535 * <script type="text/javascript">
16539 * @class Roo.data.JsonReader
16540 * @extends Roo.data.DataReader
16541 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16542 * based on mappings in a provided Roo.data.Record constructor.
16544 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16545 * in the reply previously.
16550 var RecordDef = Roo.data.Record.create([
16551 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16552 {name: 'occupation'} // This field will use "occupation" as the mapping.
16554 var myReader = new Roo.data.JsonReader({
16555 totalProperty: "results", // The property which contains the total dataset size (optional)
16556 root: "rows", // The property which contains an Array of row objects
16557 id: "id" // The property within each row object that provides an ID for the record (optional)
16561 * This would consume a JSON file like this:
16563 { 'results': 2, 'rows': [
16564 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16565 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16568 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16569 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16570 * paged from the remote server.
16571 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16572 * @cfg {String} root name of the property which contains the Array of row objects.
16573 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16574 * @cfg {Array} fields Array of field definition objects
16576 * Create a new JsonReader
16577 * @param {Object} meta Metadata configuration options
16578 * @param {Object} recordType Either an Array of field definition objects,
16579 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16581 Roo.data.JsonReader = function(meta, recordType){
16584 // set some defaults:
16585 Roo.applyIf(meta, {
16586 totalProperty: 'total',
16587 successProperty : 'success',
16592 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16594 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16596 readerType : 'Json',
16599 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16600 * Used by Store query builder to append _requestMeta to params.
16603 metaFromRemote : false,
16605 * This method is only used by a DataProxy which has retrieved data from a remote server.
16606 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16607 * @return {Object} data A data block which is used by an Roo.data.Store object as
16608 * a cache of Roo.data.Records.
16610 read : function(response){
16611 var json = response.responseText;
16613 var o = /* eval:var:o */ eval("("+json+")");
16615 throw {message: "JsonReader.read: Json object not found"};
16621 this.metaFromRemote = true;
16622 this.meta = o.metaData;
16623 this.recordType = Roo.data.Record.create(o.metaData.fields);
16624 this.onMetaChange(this.meta, this.recordType, o);
16626 return this.readRecords(o);
16629 // private function a store will implement
16630 onMetaChange : function(meta, recordType, o){
16637 simpleAccess: function(obj, subsc) {
16644 getJsonAccessor: function(){
16646 return function(expr) {
16648 return(re.test(expr))
16649 ? new Function("obj", "return obj." + expr)
16654 return Roo.emptyFn;
16659 * Create a data block containing Roo.data.Records from an XML document.
16660 * @param {Object} o An object which contains an Array of row objects in the property specified
16661 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16662 * which contains the total size of the dataset.
16663 * @return {Object} data A data block which is used by an Roo.data.Store object as
16664 * a cache of Roo.data.Records.
16666 readRecords : function(o){
16668 * After any data loads, the raw JSON data is available for further custom processing.
16672 var s = this.meta, Record = this.recordType,
16673 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16675 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16677 if(s.totalProperty) {
16678 this.getTotal = this.getJsonAccessor(s.totalProperty);
16680 if(s.successProperty) {
16681 this.getSuccess = this.getJsonAccessor(s.successProperty);
16683 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16685 var g = this.getJsonAccessor(s.id);
16686 this.getId = function(rec) {
16688 return (r === undefined || r === "") ? null : r;
16691 this.getId = function(){return null;};
16694 for(var jj = 0; jj < fl; jj++){
16696 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16697 this.ef[jj] = this.getJsonAccessor(map);
16701 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16702 if(s.totalProperty){
16703 var vt = parseInt(this.getTotal(o), 10);
16708 if(s.successProperty){
16709 var vs = this.getSuccess(o);
16710 if(vs === false || vs === 'false'){
16715 for(var i = 0; i < c; i++){
16718 var id = this.getId(n);
16719 for(var j = 0; j < fl; j++){
16721 var v = this.ef[j](n);
16723 Roo.log('missing convert for ' + f.name);
16727 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16731 raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16737 var record = new Record(values, id);
16739 records[i] = record;
16745 totalRecords : totalRecords
16748 // used when loading children.. @see loadDataFromChildren
16749 toLoadData: function(rec)
16751 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16752 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16753 return { data : data, total : data.length };
16758 * Ext JS Library 1.1.1
16759 * Copyright(c) 2006-2007, Ext JS, LLC.
16761 * Originally Released Under LGPL - original licence link has changed is not relivant.
16764 * <script type="text/javascript">
16768 * @class Roo.data.ArrayReader
16769 * @extends Roo.data.DataReader
16770 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16771 * Each element of that Array represents a row of data fields. The
16772 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16773 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16777 var RecordDef = Roo.data.Record.create([
16778 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16779 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16781 var myReader = new Roo.data.ArrayReader({
16782 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16786 * This would consume an Array like this:
16788 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16792 * Create a new JsonReader
16793 * @param {Object} meta Metadata configuration options.
16794 * @param {Object|Array} recordType Either an Array of field definition objects
16796 * @cfg {Array} fields Array of field definition objects
16797 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16798 * as specified to {@link Roo.data.Record#create},
16799 * or an {@link Roo.data.Record} object
16802 * created using {@link Roo.data.Record#create}.
16804 Roo.data.ArrayReader = function(meta, recordType)
16806 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16809 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16812 * Create a data block containing Roo.data.Records from an XML document.
16813 * @param {Object} o An Array of row objects which represents the dataset.
16814 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16815 * a cache of Roo.data.Records.
16817 readRecords : function(o)
16819 var sid = this.meta ? this.meta.id : null;
16820 var recordType = this.recordType, fields = recordType.prototype.fields;
16823 for(var i = 0; i < root.length; i++){
16826 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16827 for(var j = 0, jlen = fields.length; j < jlen; j++){
16828 var f = fields.items[j];
16829 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16830 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16832 values[f.name] = v;
16834 var record = new recordType(values, id);
16836 records[records.length] = record;
16840 totalRecords : records.length
16843 // used when loading children.. @see loadDataFromChildren
16844 toLoadData: function(rec)
16846 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16847 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16858 * @class Roo.bootstrap.form.ComboBox
16859 * @extends Roo.bootstrap.form.TriggerField
16860 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16861 * @cfg {Boolean} append (true|false) default false
16862 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16863 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16864 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16865 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16866 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16867 * @cfg {Boolean} animate default true
16868 * @cfg {Boolean} emptyResultText only for touch device
16869 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16870 * @cfg {String} emptyTitle default ''
16871 * @cfg {Number} width fixed with? experimental
16873 * Create a new ComboBox.
16874 * @param {Object} config Configuration options
16876 Roo.bootstrap.form.ComboBox = function(config){
16877 Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16881 * Fires when the dropdown list is expanded
16882 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16887 * Fires when the dropdown list is collapsed
16888 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16892 * @event beforeselect
16893 * Fires before a list item is selected. Return false to cancel the selection.
16894 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16895 * @param {Roo.data.Record} record The data record returned from the underlying store
16896 * @param {Number} index The index of the selected item in the dropdown list
16898 'beforeselect' : true,
16901 * Fires when a list item is selected
16902 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16903 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16904 * @param {Number} index The index of the selected item in the dropdown list
16908 * @event beforequery
16909 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16910 * The event object passed has these properties:
16911 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16912 * @param {String} query The query
16913 * @param {Boolean} forceAll true to force "all" query
16914 * @param {Boolean} cancel true to cancel the query
16915 * @param {Object} e The query event object
16917 'beforequery': true,
16920 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16921 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16926 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16927 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16928 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16933 * Fires when the remove value from the combobox array
16934 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16938 * @event afterremove
16939 * Fires when the remove value from the combobox array
16940 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16942 'afterremove' : true,
16944 * @event specialfilter
16945 * Fires when specialfilter
16946 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16948 'specialfilter' : true,
16951 * Fires when tick the element
16952 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16956 * @event touchviewdisplay
16957 * Fires when touch view require special display (default is using displayField)
16958 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16959 * @param {Object} cfg set html .
16961 'touchviewdisplay' : true
16966 this.tickItems = [];
16968 this.selectedIndex = -1;
16969 if(this.mode == 'local'){
16970 if(config.queryDelay === undefined){
16971 this.queryDelay = 10;
16973 if(config.minChars === undefined){
16979 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16982 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16983 * rendering into an Roo.Editor, defaults to false)
16986 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16987 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16990 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16993 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16994 * the dropdown list (defaults to undefined, with no header element)
16998 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
17002 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
17004 listWidth: undefined,
17006 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
17007 * mode = 'remote' or 'text' if mode = 'local')
17009 displayField: undefined,
17012 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
17013 * mode = 'remote' or 'value' if mode = 'local').
17014 * Note: use of a valueField requires the user make a selection
17015 * in order for a value to be mapped.
17017 valueField: undefined,
17019 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
17024 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
17025 * field's data value (defaults to the underlying DOM element's name)
17027 hiddenName: undefined,
17029 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17033 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17035 selectedClass: 'active',
17038 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17042 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17043 * anchor positions (defaults to 'tl-bl')
17045 listAlign: 'tl-bl?',
17047 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17051 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
17052 * query specified by the allQuery config option (defaults to 'query')
17054 triggerAction: 'query',
17056 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17057 * (defaults to 4, does not apply if editable = false)
17061 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17062 * delay (typeAheadDelay) if it matches a known value (defaults to false)
17066 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17067 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17071 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17072 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
17076 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
17077 * when editable = true (defaults to false)
17079 selectOnFocus:false,
17081 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17083 queryParam: 'query',
17085 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
17086 * when mode = 'remote' (defaults to 'Loading...')
17088 loadingText: 'Loading...',
17090 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17094 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17098 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17099 * traditional select (defaults to true)
17103 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17107 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17111 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17112 * listWidth has a higher value)
17116 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17117 * allow the user to set arbitrary text into the field (defaults to false)
17119 forceSelection:false,
17121 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17122 * if typeAhead = true (defaults to 250)
17124 typeAheadDelay : 250,
17126 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17127 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17129 valueNotFoundText : undefined,
17131 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17133 blockFocus : false,
17136 * @cfg {Boolean} disableClear Disable showing of clear button.
17138 disableClear : false,
17140 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
17142 alwaysQuery : false,
17145 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
17150 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17152 invalidClass : "has-warning",
17155 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17157 validClass : "has-success",
17160 * @cfg {Boolean} specialFilter (true|false) special filter default false
17162 specialFilter : false,
17165 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17167 mobileTouchView : true,
17170 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17172 useNativeIOS : false,
17175 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17177 mobile_restrict_height : false,
17179 ios_options : false,
17191 btnPosition : 'right',
17192 triggerList : true,
17193 showToggleBtn : true,
17195 emptyResultText: 'Empty',
17196 triggerText : 'Select',
17200 // element that contains real text value.. (when hidden is used..)
17202 getAutoCreate : function()
17207 * Render classic select for iso
17210 if(Roo.isIOS && this.useNativeIOS){
17211 cfg = this.getAutoCreateNativeIOS();
17219 if(Roo.isTouch && this.mobileTouchView){
17220 cfg = this.getAutoCreateTouchView();
17227 if(!this.tickable){
17228 cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17233 * ComboBox with tickable selections
17236 var align = this.labelAlign || this.parentLabelAlign();
17239 cls : 'form-group roo-combobox-tickable' //input-group
17242 var btn_text_select = '';
17243 var btn_text_done = '';
17244 var btn_text_cancel = '';
17246 if (this.btn_text_show) {
17247 btn_text_select = 'Select';
17248 btn_text_done = 'Done';
17249 btn_text_cancel = 'Cancel';
17254 cls : 'tickable-buttons',
17259 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17260 //html : this.triggerText
17261 html: btn_text_select
17267 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17269 html: btn_text_done
17275 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17277 html: btn_text_cancel
17283 buttons.cn.unshift({
17285 cls: 'roo-select2-search-field-input'
17291 Roo.each(buttons.cn, function(c){
17293 c.cls += ' btn-' + _this.size;
17296 if (_this.disabled) {
17303 style : 'display: contents',
17308 cls: 'form-hidden-field'
17312 cls: 'roo-select2-choices',
17316 cls: 'roo-select2-search-field',
17327 cls: 'roo-select2-container input-group roo-select2-container-multi',
17333 // cls: 'typeahead typeahead-long dropdown-menu',
17334 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
17339 if(this.hasFeedback && !this.allowBlank){
17343 cls: 'glyphicon form-control-feedback'
17346 combobox.cn.push(feedback);
17353 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17354 tooltip : 'This field is required'
17357 if (this.allowBlank) {
17360 style : 'display:none'
17363 if (align ==='left' && this.fieldLabel.length) {
17365 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
17372 cls : 'control-label col-form-label',
17373 html : this.fieldLabel
17385 var labelCfg = cfg.cn[1];
17386 var contentCfg = cfg.cn[2];
17389 if(this.indicatorpos == 'right'){
17395 cls : 'control-label col-form-label',
17399 html : this.fieldLabel
17415 labelCfg = cfg.cn[0];
17416 contentCfg = cfg.cn[1];
17420 if(this.labelWidth > 12){
17421 labelCfg.style = "width: " + this.labelWidth + 'px';
17423 if(this.width * 1 > 0){
17424 contentCfg.style = "width: " + this.width + 'px';
17426 if(this.labelWidth < 13 && this.labelmd == 0){
17427 this.labelmd = this.labelWidth;
17430 if(this.labellg > 0){
17431 labelCfg.cls += ' col-lg-' + this.labellg;
17432 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17435 if(this.labelmd > 0){
17436 labelCfg.cls += ' col-md-' + this.labelmd;
17437 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17440 if(this.labelsm > 0){
17441 labelCfg.cls += ' col-sm-' + this.labelsm;
17442 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17445 if(this.labelxs > 0){
17446 labelCfg.cls += ' col-xs-' + this.labelxs;
17447 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17451 } else if ( this.fieldLabel.length) {
17452 // Roo.log(" label");
17457 //cls : 'input-group-addon',
17458 html : this.fieldLabel
17463 if(this.indicatorpos == 'right'){
17467 //cls : 'input-group-addon',
17468 html : this.fieldLabel
17478 // Roo.log(" no label && no align");
17485 ['xs','sm','md','lg'].map(function(size){
17486 if (settings[size]) {
17487 cfg.cls += ' col-' + size + '-' + settings[size];
17495 _initEventsCalled : false,
17498 initEvents: function()
17500 if (this._initEventsCalled) { // as we call render... prevent looping...
17503 this._initEventsCalled = true;
17506 throw "can not find store for combo";
17509 this.indicator = this.indicatorEl();
17511 this.store = Roo.factory(this.store, Roo.data);
17512 this.store.parent = this;
17514 // if we are building from html. then this element is so complex, that we can not really
17515 // use the rendered HTML.
17516 // so we have to trash and replace the previous code.
17517 if (Roo.XComponent.build_from_html) {
17518 // remove this element....
17519 var e = this.el.dom, k=0;
17520 while (e ) { e = e.previousSibling; ++k;}
17525 this.rendered = false;
17527 this.render(this.parent().getChildContainer(true), k);
17530 if(Roo.isIOS && this.useNativeIOS){
17531 this.initIOSView();
17539 if(Roo.isTouch && this.mobileTouchView){
17540 this.initTouchView();
17545 this.initTickableEvents();
17549 Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17551 if(this.hiddenName){
17553 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17555 this.hiddenField.dom.value =
17556 this.hiddenValue !== undefined ? this.hiddenValue :
17557 this.value !== undefined ? this.value : '';
17559 // prevent input submission
17560 this.el.dom.removeAttribute('name');
17561 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17566 // this.el.dom.setAttribute('autocomplete', 'off');
17569 var cls = 'x-combo-list';
17571 //this.list = new Roo.Layer({
17572 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17578 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17579 _this.list.setWidth(lw);
17582 this.list.on('mouseover', this.onViewOver, this);
17583 this.list.on('mousemove', this.onViewMove, this);
17584 this.list.on('scroll', this.onViewScroll, this);
17587 this.list.swallowEvent('mousewheel');
17588 this.assetHeight = 0;
17591 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17592 this.assetHeight += this.header.getHeight();
17595 this.innerList = this.list.createChild({cls:cls+'-inner'});
17596 this.innerList.on('mouseover', this.onViewOver, this);
17597 this.innerList.on('mousemove', this.onViewMove, this);
17598 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17600 if(this.allowBlank && !this.pageSize && !this.disableClear){
17601 this.footer = this.list.createChild({cls:cls+'-ft'});
17602 this.pageTb = new Roo.Toolbar(this.footer);
17606 this.footer = this.list.createChild({cls:cls+'-ft'});
17607 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17608 {pageSize: this.pageSize});
17612 if (this.pageTb && this.allowBlank && !this.disableClear) {
17614 this.pageTb.add(new Roo.Toolbar.Fill(), {
17615 cls: 'x-btn-icon x-btn-clear',
17617 handler: function()
17620 _this.clearValue();
17621 _this.onSelect(false, -1);
17626 this.assetHeight += this.footer.getHeight();
17631 this.tpl = Roo.bootstrap.version == 4 ?
17632 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17633 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17636 this.view = new Roo.View(this.list, this.tpl, {
17637 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17639 //this.view.wrapEl.setDisplayed(false);
17640 this.view.on('click', this.onViewClick, this);
17643 this.store.on('beforeload', this.onBeforeLoad, this);
17644 this.store.on('load', this.onLoad, this);
17645 this.store.on('loadexception', this.onLoadException, this);
17647 if(this.resizable){
17648 this.resizer = new Roo.Resizable(this.list, {
17649 pinned:true, handles:'se'
17651 this.resizer.on('resize', function(r, w, h){
17652 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17653 this.listWidth = w;
17654 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17655 this.restrictHeight();
17657 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17660 if(!this.editable){
17661 this.editable = true;
17662 this.setEditable(false);
17667 if (typeof(this.events.add.listeners) != 'undefined') {
17669 this.addicon = this.wrap.createChild(
17670 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17672 this.addicon.on('click', function(e) {
17673 this.fireEvent('add', this);
17676 if (typeof(this.events.edit.listeners) != 'undefined') {
17678 this.editicon = this.wrap.createChild(
17679 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17680 if (this.addicon) {
17681 this.editicon.setStyle('margin-left', '40px');
17683 this.editicon.on('click', function(e) {
17685 // we fire even if inothing is selected..
17686 this.fireEvent('edit', this, this.lastData );
17692 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17693 "up" : function(e){
17694 this.inKeyMode = true;
17698 "down" : function(e){
17699 if(!this.isExpanded()){
17700 this.onTriggerClick();
17702 this.inKeyMode = true;
17707 "enter" : function(e){
17708 // this.onViewClick();
17712 if(this.fireEvent("specialkey", this, e)){
17713 this.onViewClick(false);
17719 "esc" : function(e){
17723 "tab" : function(e){
17726 if(this.fireEvent("specialkey", this, e)){
17727 this.onViewClick(false);
17735 doRelay : function(foo, bar, hname){
17736 if(hname == 'down' || this.scope.isExpanded()){
17737 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17746 this.queryDelay = Math.max(this.queryDelay || 10,
17747 this.mode == 'local' ? 10 : 250);
17750 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17752 if(this.typeAhead){
17753 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17755 if(this.editable !== false){
17756 this.inputEl().on("keyup", this.onKeyUp, this);
17758 if(this.forceSelection){
17759 this.inputEl().on('blur', this.doForce, this);
17763 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17764 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17768 initTickableEvents: function()
17772 if(this.hiddenName){
17774 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17776 this.hiddenField.dom.value =
17777 this.hiddenValue !== undefined ? this.hiddenValue :
17778 this.value !== undefined ? this.value : '';
17780 // prevent input submission
17781 this.el.dom.removeAttribute('name');
17782 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17787 // this.list = this.el.select('ul.dropdown-menu',true).first();
17789 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17790 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17791 if(this.triggerList){
17792 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17795 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17796 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17798 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17799 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17801 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17802 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17804 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17805 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17806 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17809 this.cancelBtn.hide();
17814 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17815 _this.list.setWidth(lw);
17818 this.list.on('mouseover', this.onViewOver, this);
17819 this.list.on('mousemove', this.onViewMove, this);
17821 this.list.on('scroll', this.onViewScroll, this);
17824 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17825 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17828 this.view = new Roo.View(this.list, this.tpl, {
17833 selectedClass: this.selectedClass
17836 //this.view.wrapEl.setDisplayed(false);
17837 this.view.on('click', this.onViewClick, this);
17841 this.store.on('beforeload', this.onBeforeLoad, this);
17842 this.store.on('load', this.onLoad, this);
17843 this.store.on('loadexception', this.onLoadException, this);
17846 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17847 "up" : function(e){
17848 this.inKeyMode = true;
17852 "down" : function(e){
17853 this.inKeyMode = true;
17857 "enter" : function(e){
17858 if(this.fireEvent("specialkey", this, e)){
17859 this.onViewClick(false);
17865 "esc" : function(e){
17866 this.onTickableFooterButtonClick(e, false, false);
17869 "tab" : function(e){
17870 this.fireEvent("specialkey", this, e);
17872 this.onTickableFooterButtonClick(e, false, false);
17879 doRelay : function(e, fn, key){
17880 if(this.scope.isExpanded()){
17881 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17890 this.queryDelay = Math.max(this.queryDelay || 10,
17891 this.mode == 'local' ? 10 : 250);
17894 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17896 if(this.typeAhead){
17897 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17900 if(this.editable !== false){
17901 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17904 this.indicator = this.indicatorEl();
17906 if(this.indicator){
17907 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17908 this.indicator.hide();
17913 onDestroy : function(){
17915 this.view.setStore(null);
17916 this.view.el.removeAllListeners();
17917 this.view.el.remove();
17918 this.view.purgeListeners();
17921 this.list.dom.innerHTML = '';
17925 this.store.un('beforeload', this.onBeforeLoad, this);
17926 this.store.un('load', this.onLoad, this);
17927 this.store.un('loadexception', this.onLoadException, this);
17929 Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17933 fireKey : function(e){
17934 if(e.isNavKeyPress() && !this.list.isVisible()){
17935 this.fireEvent("specialkey", this, e);
17940 onResize: function(w, h)
17944 // Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17946 // if(typeof w != 'number'){
17947 // // we do not handle it!?!?
17950 // var tw = this.trigger.getWidth();
17951 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17952 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17954 // this.inputEl().setWidth( this.adjustWidth('input', x));
17956 // //this.trigger.setStyle('left', x+'px');
17958 // if(this.list && this.listWidth === undefined){
17959 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17960 // this.list.setWidth(lw);
17961 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17969 * Allow or prevent the user from directly editing the field text. If false is passed,
17970 * the user will only be able to select from the items defined in the dropdown list. This method
17971 * is the runtime equivalent of setting the 'editable' config option at config time.
17972 * @param {Boolean} value True to allow the user to directly edit the field text
17974 setEditable : function(value){
17975 if(value == this.editable){
17978 this.editable = value;
17980 this.inputEl().dom.setAttribute('readOnly', true);
17981 this.inputEl().on('mousedown', this.onTriggerClick, this);
17982 this.inputEl().addClass('x-combo-noedit');
17984 this.inputEl().dom.removeAttribute('readOnly');
17985 this.inputEl().un('mousedown', this.onTriggerClick, this);
17986 this.inputEl().removeClass('x-combo-noedit');
17992 onBeforeLoad : function(combo,opts){
17993 if(!this.hasFocus){
17997 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17999 this.restrictHeight();
18000 this.selectedIndex = -1;
18004 onLoad : function(){
18006 this.hasQuery = false;
18008 if(!this.hasFocus){
18012 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18013 this.loading.hide();
18016 if(this.store.getCount() > 0){
18019 this.restrictHeight();
18020 if(this.lastQuery == this.allQuery){
18021 if(this.editable && !this.tickable){
18022 this.inputEl().dom.select();
18026 !this.selectByValue(this.value, true) &&
18029 !this.store.lastOptions ||
18030 typeof(this.store.lastOptions.add) == 'undefined' ||
18031 this.store.lastOptions.add != true
18034 this.select(0, true);
18037 if(this.autoFocus){
18040 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18041 this.taTask.delay(this.typeAheadDelay);
18045 this.onEmptyResults();
18051 onLoadException : function()
18053 this.hasQuery = false;
18055 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18056 this.loading.hide();
18059 if(this.tickable && this.editable){
18064 // only causes errors at present
18065 //Roo.log(this.store.reader.jsonData);
18066 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18068 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18074 onTypeAhead : function(){
18075 if(this.store.getCount() > 0){
18076 var r = this.store.getAt(0);
18077 var newValue = r.data[this.displayField];
18078 var len = newValue.length;
18079 var selStart = this.getRawValue().length;
18081 if(selStart != len){
18082 this.setRawValue(newValue);
18083 this.selectText(selStart, newValue.length);
18089 onSelect : function(record, index){
18091 if(this.fireEvent('beforeselect', this, record, index) !== false){
18093 this.setFromData(index > -1 ? record.data : false);
18096 this.fireEvent('select', this, record, index);
18101 * Returns the currently selected field value or empty string if no value is set.
18102 * @return {String} value The selected value
18104 getValue : function()
18106 if(Roo.isIOS && this.useNativeIOS){
18107 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18111 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18114 if(this.valueField){
18115 return typeof this.value != 'undefined' ? this.value : '';
18117 return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18121 getRawValue : function()
18123 if(Roo.isIOS && this.useNativeIOS){
18124 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18127 var v = this.inputEl().getValue();
18133 * Clears any text/value currently set in the field
18135 clearValue : function(){
18137 if(this.hiddenField){
18138 this.hiddenField.dom.value = '';
18141 this.setRawValue('');
18142 this.lastSelectionText = '';
18143 this.lastData = false;
18145 var close = this.closeTriggerEl();
18156 * Sets the specified value into the field. If the value finds a match, the corresponding record text
18157 * will be displayed in the field. If the value does not match the data value of an existing item,
18158 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18159 * Otherwise the field will be blank (although the value will still be set).
18160 * @param {String} value The value to match
18162 setValue : function(v)
18164 if(Roo.isIOS && this.useNativeIOS){
18165 this.setIOSValue(v);
18175 if(this.valueField){
18176 var r = this.findRecord(this.valueField, v);
18178 text = r.data[this.displayField];
18179 }else if(this.valueNotFoundText !== undefined){
18180 text = this.valueNotFoundText;
18183 this.lastSelectionText = text;
18184 if(this.hiddenField){
18185 this.hiddenField.dom.value = v;
18187 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18190 var close = this.closeTriggerEl();
18193 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18199 * @property {Object} the last set data for the element
18204 * Sets the value of the field based on a object which is related to the record format for the store.
18205 * @param {Object} value the value to set as. or false on reset?
18207 setFromData : function(o){
18214 var dv = ''; // display value
18215 var vv = ''; // value value..
18217 if (this.displayField) {
18218 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18220 // this is an error condition!!!
18221 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18224 if(this.valueField){
18225 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18228 var close = this.closeTriggerEl();
18231 if(dv.length || vv * 1 > 0){
18233 this.blockFocus=true;
18239 if(this.hiddenField){
18240 this.hiddenField.dom.value = vv;
18242 this.lastSelectionText = dv;
18243 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18247 // no hidden field.. - we store the value in 'value', but still display
18248 // display field!!!!
18249 this.lastSelectionText = dv;
18250 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18257 reset : function(){
18258 // overridden so that last data is reset..
18265 this.setValue(this.originalValue);
18266 //this.clearInvalid();
18267 this.lastData = false;
18269 this.view.clearSelections();
18275 findRecord : function(prop, value){
18277 if(this.store.getCount() > 0){
18278 this.store.each(function(r){
18279 if(r.data[prop] == value){
18289 getName: function()
18291 // returns hidden if it's set..
18292 if (!this.rendered) {return ''};
18293 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
18297 onViewMove : function(e, t){
18298 this.inKeyMode = false;
18302 onViewOver : function(e, t){
18303 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18306 var item = this.view.findItemFromChild(t);
18309 var index = this.view.indexOf(item);
18310 this.select(index, false);
18315 onViewClick : function(view, doFocus, el, e)
18317 var index = this.view.getSelectedIndexes()[0];
18319 var r = this.store.getAt(index);
18323 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18330 Roo.each(this.tickItems, function(v,k){
18332 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18334 _this.tickItems.splice(k, 1);
18336 if(typeof(e) == 'undefined' && view == false){
18337 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18349 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18350 this.tickItems.push(r.data);
18353 if(typeof(e) == 'undefined' && view == false){
18354 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18361 this.onSelect(r, index);
18363 if(doFocus !== false && !this.blockFocus){
18364 this.inputEl().focus();
18369 restrictHeight : function(){
18370 //this.innerList.dom.style.height = '';
18371 //var inner = this.innerList.dom;
18372 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18373 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18374 //this.list.beginUpdate();
18375 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18376 this.list.alignTo(this.inputEl(), this.listAlign);
18377 this.list.alignTo(this.inputEl(), this.listAlign);
18378 //this.list.endUpdate();
18382 onEmptyResults : function(){
18384 if(this.tickable && this.editable){
18385 this.hasFocus = false;
18386 this.restrictHeight();
18394 * Returns true if the dropdown list is expanded, else false.
18396 isExpanded : function(){
18397 return this.list.isVisible();
18401 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18402 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18403 * @param {String} value The data value of the item to select
18404 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18405 * selected item if it is not currently in view (defaults to true)
18406 * @return {Boolean} True if the value matched an item in the list, else false
18408 selectByValue : function(v, scrollIntoView){
18409 if(v !== undefined && v !== null){
18410 var r = this.findRecord(this.valueField || this.displayField, v);
18412 this.select(this.store.indexOf(r), scrollIntoView);
18420 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18421 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18422 * @param {Number} index The zero-based index of the list item to select
18423 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18424 * selected item if it is not currently in view (defaults to true)
18426 select : function(index, scrollIntoView){
18427 this.selectedIndex = index;
18428 this.view.select(index);
18429 if(scrollIntoView !== false){
18430 var el = this.view.getNode(index);
18432 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18435 this.list.scrollChildIntoView(el, false);
18441 selectNext : function(){
18442 var ct = this.store.getCount();
18444 if(this.selectedIndex == -1){
18446 }else if(this.selectedIndex < ct-1){
18447 this.select(this.selectedIndex+1);
18453 selectPrev : function(){
18454 var ct = this.store.getCount();
18456 if(this.selectedIndex == -1){
18458 }else if(this.selectedIndex != 0){
18459 this.select(this.selectedIndex-1);
18465 onKeyUp : function(e){
18466 if(this.editable !== false && !e.isSpecialKey()){
18467 this.lastKey = e.getKey();
18468 this.dqTask.delay(this.queryDelay);
18473 validateBlur : function(){
18474 return !this.list || !this.list.isVisible();
18478 initQuery : function(){
18480 var v = this.getRawValue();
18482 if(this.tickable && this.editable){
18483 v = this.tickableInputEl().getValue();
18490 doForce : function(){
18491 if(this.inputEl().dom.value.length > 0){
18492 this.inputEl().dom.value =
18493 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18499 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
18500 * query allowing the query action to be canceled if needed.
18501 * @param {String} query The SQL query to execute
18502 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18503 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18504 * saved in the current store (defaults to false)
18506 doQuery : function(q, forceAll){
18508 if(q === undefined || q === null){
18513 forceAll: forceAll,
18517 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18522 forceAll = qe.forceAll;
18523 if(forceAll === true || (q.length >= this.minChars)){
18525 this.hasQuery = true;
18527 if(this.lastQuery != q || this.alwaysQuery){
18528 this.lastQuery = q;
18529 if(this.mode == 'local'){
18530 this.selectedIndex = -1;
18532 this.store.clearFilter();
18535 if(this.specialFilter){
18536 this.fireEvent('specialfilter', this);
18541 this.store.filter(this.displayField, q);
18544 this.store.fireEvent("datachanged", this.store);
18551 this.store.baseParams[this.queryParam] = q;
18553 var options = {params : this.getParams(q)};
18556 options.add = true;
18557 options.params.start = this.page * this.pageSize;
18560 this.store.load(options);
18563 * this code will make the page width larger, at the beginning, the list not align correctly,
18564 * we should expand the list on onLoad
18565 * so command out it
18570 this.selectedIndex = -1;
18575 this.loadNext = false;
18579 getParams : function(q){
18581 //p[this.queryParam] = q;
18585 p.limit = this.pageSize;
18591 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18593 collapse : function(){
18594 if(!this.isExpanded()){
18600 this.hasFocus = false;
18604 this.cancelBtn.hide();
18605 this.trigger.show();
18608 this.tickableInputEl().dom.value = '';
18609 this.tickableInputEl().blur();
18614 Roo.get(document).un('mousedown', this.collapseIf, this);
18615 Roo.get(document).un('mousewheel', this.collapseIf, this);
18616 if (!this.editable) {
18617 Roo.get(document).un('keydown', this.listKeyPress, this);
18619 this.fireEvent('collapse', this);
18625 collapseIf : function(e){
18626 var in_combo = e.within(this.el);
18627 var in_list = e.within(this.list);
18628 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18630 if (in_combo || in_list || is_list) {
18631 //e.stopPropagation();
18636 this.onTickableFooterButtonClick(e, false, false);
18644 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18646 expand : function(){
18648 if(this.isExpanded() || !this.hasFocus){
18652 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18653 this.list.setWidth(lw);
18659 this.restrictHeight();
18663 this.tickItems = Roo.apply([], this.item);
18666 this.cancelBtn.show();
18667 this.trigger.hide();
18670 this.tickableInputEl().focus();
18675 Roo.get(document).on('mousedown', this.collapseIf, this);
18676 Roo.get(document).on('mousewheel', this.collapseIf, this);
18677 if (!this.editable) {
18678 Roo.get(document).on('keydown', this.listKeyPress, this);
18681 this.fireEvent('expand', this);
18685 // Implements the default empty TriggerField.onTriggerClick function
18686 onTriggerClick : function(e)
18688 Roo.log('trigger click');
18690 if(this.disabled || !this.triggerList){
18695 this.loadNext = false;
18697 if(this.isExpanded()){
18699 if (!this.blockFocus) {
18700 this.inputEl().focus();
18704 this.hasFocus = true;
18705 if(this.triggerAction == 'all') {
18706 this.doQuery(this.allQuery, true);
18708 this.doQuery(this.getRawValue());
18710 if (!this.blockFocus) {
18711 this.inputEl().focus();
18716 onTickableTriggerClick : function(e)
18723 this.loadNext = false;
18724 this.hasFocus = true;
18726 if(this.triggerAction == 'all') {
18727 this.doQuery(this.allQuery, true);
18729 this.doQuery(this.getRawValue());
18733 onSearchFieldClick : function(e)
18735 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18736 this.onTickableFooterButtonClick(e, false, false);
18740 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18745 this.loadNext = false;
18746 this.hasFocus = true;
18748 if(this.triggerAction == 'all') {
18749 this.doQuery(this.allQuery, true);
18751 this.doQuery(this.getRawValue());
18755 listKeyPress : function(e)
18757 //Roo.log('listkeypress');
18758 // scroll to first matching element based on key pres..
18759 if (e.isSpecialKey()) {
18762 var k = String.fromCharCode(e.getKey()).toUpperCase();
18765 var csel = this.view.getSelectedNodes();
18766 var cselitem = false;
18768 var ix = this.view.indexOf(csel[0]);
18769 cselitem = this.store.getAt(ix);
18770 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18776 this.store.each(function(v) {
18778 // start at existing selection.
18779 if (cselitem.id == v.id) {
18785 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18786 match = this.store.indexOf(v);
18792 if (match === false) {
18793 return true; // no more action?
18796 this.view.select(match);
18797 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18798 sn.scrollIntoView(sn.dom.parentNode, false);
18801 onViewScroll : function(e, t){
18803 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){
18807 this.hasQuery = true;
18809 this.loading = this.list.select('.loading', true).first();
18811 if(this.loading === null){
18812 this.list.createChild({
18814 cls: 'loading roo-select2-more-results roo-select2-active',
18815 html: 'Loading more results...'
18818 this.loading = this.list.select('.loading', true).first();
18820 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18822 this.loading.hide();
18825 this.loading.show();
18830 this.loadNext = true;
18832 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18837 addItem : function(o)
18839 var dv = ''; // display value
18841 if (this.displayField) {
18842 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18844 // this is an error condition!!!
18845 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18852 var choice = this.choices.createChild({
18854 cls: 'roo-select2-search-choice',
18863 cls: 'roo-select2-search-choice-close fa fa-times',
18868 }, this.searchField);
18870 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18872 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18880 this.inputEl().dom.value = '';
18885 onRemoveItem : function(e, _self, o)
18887 e.preventDefault();
18889 this.lastItem = Roo.apply([], this.item);
18891 var index = this.item.indexOf(o.data) * 1;
18894 Roo.log('not this item?!');
18898 this.item.splice(index, 1);
18903 this.fireEvent('remove', this, e);
18909 syncValue : function()
18911 if(!this.item.length){
18918 Roo.each(this.item, function(i){
18919 if(_this.valueField){
18920 value.push(i[_this.valueField]);
18927 this.value = value.join(',');
18929 if(this.hiddenField){
18930 this.hiddenField.dom.value = this.value;
18933 this.store.fireEvent("datachanged", this.store);
18938 clearItem : function()
18940 if(!this.multiple){
18946 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18954 if(this.tickable && !Roo.isTouch){
18955 this.view.refresh();
18959 inputEl: function ()
18961 if(Roo.isIOS && this.useNativeIOS){
18962 return this.el.select('select.roo-ios-select', true).first();
18965 if(Roo.isTouch && this.mobileTouchView){
18966 return this.el.select('input.form-control',true).first();
18970 return this.searchField;
18973 return this.el.select('input.form-control',true).first();
18976 onTickableFooterButtonClick : function(e, btn, el)
18978 e.preventDefault();
18980 this.lastItem = Roo.apply([], this.item);
18982 if(btn && btn.name == 'cancel'){
18983 this.tickItems = Roo.apply([], this.item);
18992 Roo.each(this.tickItems, function(o){
19000 validate : function()
19002 if(this.getVisibilityEl().hasClass('hidden')){
19006 var v = this.getRawValue();
19009 v = this.getValue();
19012 if(this.disabled || this.allowBlank || v.length){
19017 this.markInvalid();
19021 tickableInputEl : function()
19023 if(!this.tickable || !this.editable){
19024 return this.inputEl();
19027 return this.inputEl().select('.roo-select2-search-field-input', true).first();
19031 getAutoCreateTouchView : function()
19036 cls: 'form-group' //input-group
19042 type : this.inputType,
19043 cls : 'form-control x-combo-noedit',
19044 autocomplete: 'new-password',
19045 placeholder : this.placeholder || '',
19050 input.name = this.name;
19054 input.cls += ' input-' + this.size;
19057 if (this.disabled) {
19058 input.disabled = true;
19062 cls : 'roo-combobox-wrap',
19069 inputblock.cls += ' input-group';
19071 inputblock.cn.unshift({
19073 cls : 'input-group-addon input-group-prepend input-group-text',
19078 if(this.removable && !this.multiple){
19079 inputblock.cls += ' roo-removable';
19081 inputblock.cn.push({
19084 cls : 'roo-combo-removable-btn close'
19088 if(this.hasFeedback && !this.allowBlank){
19090 inputblock.cls += ' has-feedback';
19092 inputblock.cn.push({
19094 cls: 'glyphicon form-control-feedback'
19101 inputblock.cls += (this.before) ? '' : ' input-group';
19103 inputblock.cn.push({
19105 cls : 'input-group-addon input-group-append input-group-text',
19111 var ibwrap = inputblock;
19116 cls: 'roo-select2-choices',
19120 cls: 'roo-select2-search-field',
19133 cls: 'roo-select2-container input-group roo-touchview-combobox ',
19138 cls: 'form-hidden-field'
19144 if(!this.multiple && this.showToggleBtn){
19150 if (this.caret != false) {
19153 cls: 'fa fa-' + this.caret
19160 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19162 Roo.bootstrap.version == 3 ? caret : '',
19165 cls: 'combobox-clear',
19179 combobox.cls += ' roo-select2-container-multi';
19182 var required = this.allowBlank ? {
19184 style: 'display: none'
19187 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19188 tooltip : 'This field is required'
19191 var align = this.labelAlign || this.parentLabelAlign();
19193 if (align ==='left' && this.fieldLabel.length) {
19199 cls : 'control-label col-form-label',
19200 html : this.fieldLabel
19204 cls : 'roo-combobox-wrap ',
19211 var labelCfg = cfg.cn[1];
19212 var contentCfg = cfg.cn[2];
19215 if(this.indicatorpos == 'right'){
19220 cls : 'control-label col-form-label',
19224 html : this.fieldLabel
19230 cls : "roo-combobox-wrap ",
19238 labelCfg = cfg.cn[0];
19239 contentCfg = cfg.cn[1];
19244 if(this.labelWidth > 12){
19245 labelCfg.style = "width: " + this.labelWidth + 'px';
19248 if(this.labelWidth < 13 && this.labelmd == 0){
19249 this.labelmd = this.labelWidth;
19252 if(this.labellg > 0){
19253 labelCfg.cls += ' col-lg-' + this.labellg;
19254 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19257 if(this.labelmd > 0){
19258 labelCfg.cls += ' col-md-' + this.labelmd;
19259 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19262 if(this.labelsm > 0){
19263 labelCfg.cls += ' col-sm-' + this.labelsm;
19264 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19267 if(this.labelxs > 0){
19268 labelCfg.cls += ' col-xs-' + this.labelxs;
19269 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19273 } else if ( this.fieldLabel.length) {
19278 cls : 'control-label',
19279 html : this.fieldLabel
19290 if(this.indicatorpos == 'right'){
19294 cls : 'control-label',
19295 html : this.fieldLabel,
19313 var settings = this;
19315 ['xs','sm','md','lg'].map(function(size){
19316 if (settings[size]) {
19317 cfg.cls += ' col-' + size + '-' + settings[size];
19324 initTouchView : function()
19326 this.renderTouchView();
19328 this.touchViewEl.on('scroll', function(){
19329 this.el.dom.scrollTop = 0;
19332 this.originalValue = this.getValue();
19334 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19336 this.inputEl().on("click", this.showTouchView, this);
19337 if (this.triggerEl) {
19338 this.triggerEl.on("click", this.showTouchView, this);
19342 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19343 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19345 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19347 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19348 this.store.on('load', this.onTouchViewLoad, this);
19349 this.store.on('loadexception', this.onTouchViewLoadException, this);
19351 if(this.hiddenName){
19353 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19355 this.hiddenField.dom.value =
19356 this.hiddenValue !== undefined ? this.hiddenValue :
19357 this.value !== undefined ? this.value : '';
19359 this.el.dom.removeAttribute('name');
19360 this.hiddenField.dom.setAttribute('name', this.hiddenName);
19364 this.choices = this.el.select('ul.roo-select2-choices', true).first();
19365 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19368 if(this.removable && !this.multiple){
19369 var close = this.closeTriggerEl();
19371 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19372 close.on('click', this.removeBtnClick, this, close);
19376 * fix the bug in Safari iOS8
19378 this.inputEl().on("focus", function(e){
19379 document.activeElement.blur();
19382 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19389 renderTouchView : function()
19391 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19392 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19394 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19395 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19397 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19398 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19399 this.touchViewBodyEl.setStyle('overflow', 'auto');
19401 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19402 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19404 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19405 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19409 showTouchView : function()
19415 this.touchViewHeaderEl.hide();
19417 if(this.modalTitle.length){
19418 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19419 this.touchViewHeaderEl.show();
19422 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19423 this.touchViewEl.show();
19425 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19427 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19428 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19430 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19432 if(this.modalTitle.length){
19433 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19436 this.touchViewBodyEl.setHeight(bodyHeight);
19440 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19442 this.touchViewEl.addClass(['in','show']);
19445 if(this._touchViewMask){
19446 Roo.get(document.body).addClass("x-body-masked");
19447 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19448 this._touchViewMask.setStyle('z-index', 10000);
19449 this._touchViewMask.addClass('show');
19452 this.doTouchViewQuery();
19456 hideTouchView : function()
19458 this.touchViewEl.removeClass(['in','show']);
19462 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19464 this.touchViewEl.setStyle('display', 'none');
19467 if(this._touchViewMask){
19468 this._touchViewMask.removeClass('show');
19469 Roo.get(document.body).removeClass("x-body-masked");
19473 setTouchViewValue : function()
19480 Roo.each(this.tickItems, function(o){
19485 this.hideTouchView();
19488 doTouchViewQuery : function()
19497 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19501 if(!this.alwaysQuery || this.mode == 'local'){
19502 this.onTouchViewLoad();
19509 onTouchViewBeforeLoad : function(combo,opts)
19515 onTouchViewLoad : function()
19517 if(this.store.getCount() < 1){
19518 this.onTouchViewEmptyResults();
19522 this.clearTouchView();
19524 var rawValue = this.getRawValue();
19526 var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19528 this.tickItems = [];
19530 this.store.data.each(function(d, rowIndex){
19531 var row = this.touchViewListGroup.createChild(template);
19533 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19534 row.addClass(d.data.cls);
19537 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19540 html : d.data[this.displayField]
19543 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19544 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19547 row.removeClass('selected');
19548 if(!this.multiple && this.valueField &&
19549 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19552 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19553 row.addClass('selected');
19556 if(this.multiple && this.valueField &&
19557 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19561 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19562 this.tickItems.push(d.data);
19565 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19569 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19571 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19573 if(this.modalTitle.length){
19574 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19577 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19579 if(this.mobile_restrict_height && listHeight < bodyHeight){
19580 this.touchViewBodyEl.setHeight(listHeight);
19585 if(firstChecked && listHeight > bodyHeight){
19586 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19591 onTouchViewLoadException : function()
19593 this.hideTouchView();
19596 onTouchViewEmptyResults : function()
19598 this.clearTouchView();
19600 this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19602 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19606 clearTouchView : function()
19608 this.touchViewListGroup.dom.innerHTML = '';
19611 onTouchViewClick : function(e, el, o)
19613 e.preventDefault();
19616 var rowIndex = o.rowIndex;
19618 var r = this.store.getAt(rowIndex);
19620 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19622 if(!this.multiple){
19623 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19624 c.dom.removeAttribute('checked');
19627 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19629 this.setFromData(r.data);
19631 var close = this.closeTriggerEl();
19637 this.hideTouchView();
19639 this.fireEvent('select', this, r, rowIndex);
19644 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19645 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19646 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19650 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19651 this.addItem(r.data);
19652 this.tickItems.push(r.data);
19656 getAutoCreateNativeIOS : function()
19659 cls: 'form-group' //input-group,
19664 cls : 'roo-ios-select'
19668 combobox.name = this.name;
19671 if (this.disabled) {
19672 combobox.disabled = true;
19675 var settings = this;
19677 ['xs','sm','md','lg'].map(function(size){
19678 if (settings[size]) {
19679 cfg.cls += ' col-' + size + '-' + settings[size];
19689 initIOSView : function()
19691 this.store.on('load', this.onIOSViewLoad, this);
19696 onIOSViewLoad : function()
19698 if(this.store.getCount() < 1){
19702 this.clearIOSView();
19704 if(this.allowBlank) {
19706 var default_text = '-- SELECT --';
19708 if(this.placeholder.length){
19709 default_text = this.placeholder;
19712 if(this.emptyTitle.length){
19713 default_text += ' - ' + this.emptyTitle + ' -';
19716 var opt = this.inputEl().createChild({
19719 html : default_text
19723 o[this.valueField] = 0;
19724 o[this.displayField] = default_text;
19726 this.ios_options.push({
19733 this.store.data.each(function(d, rowIndex){
19737 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19738 html = d.data[this.displayField];
19743 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19744 value = d.data[this.valueField];
19753 if(this.value == d.data[this.valueField]){
19754 option['selected'] = true;
19757 var opt = this.inputEl().createChild(option);
19759 this.ios_options.push({
19766 this.inputEl().on('change', function(){
19767 this.fireEvent('select', this);
19772 clearIOSView: function()
19774 this.inputEl().dom.innerHTML = '';
19776 this.ios_options = [];
19779 setIOSValue: function(v)
19783 if(!this.ios_options){
19787 Roo.each(this.ios_options, function(opts){
19789 opts.el.dom.removeAttribute('selected');
19791 if(opts.data[this.valueField] != v){
19795 opts.el.dom.setAttribute('selected', true);
19801 * @cfg {Boolean} grow
19805 * @cfg {Number} growMin
19809 * @cfg {Number} growMax
19818 Roo.apply(Roo.bootstrap.form.ComboBox, {
19822 cls: 'modal-header',
19844 cls: 'list-group-item',
19848 cls: 'roo-combobox-list-group-item-value'
19852 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19866 listItemCheckbox : {
19868 cls: 'list-group-item',
19872 cls: 'roo-combobox-list-group-item-value'
19876 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19892 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19897 cls: 'modal-footer',
19905 cls: 'col-xs-6 text-left',
19908 cls: 'btn btn-danger roo-touch-view-cancel',
19914 cls: 'col-xs-6 text-right',
19917 cls: 'btn btn-success roo-touch-view-ok',
19928 Roo.apply(Roo.bootstrap.form.ComboBox, {
19930 touchViewTemplate : {
19932 cls: 'modal fade roo-combobox-touch-view',
19936 cls: 'modal-dialog',
19937 style : 'position:fixed', // we have to fix position....
19941 cls: 'modal-content',
19943 Roo.bootstrap.form.ComboBox.header,
19944 Roo.bootstrap.form.ComboBox.body,
19945 Roo.bootstrap.form.ComboBox.footer
19954 * Ext JS Library 1.1.1
19955 * Copyright(c) 2006-2007, Ext JS, LLC.
19957 * Originally Released Under LGPL - original licence link has changed is not relivant.
19960 * <script type="text/javascript">
19965 * @extends Roo.util.Observable
19966 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19967 * This class also supports single and multi selection modes. <br>
19968 * Create a data model bound view:
19970 var store = new Roo.data.Store(...);
19972 var view = new Roo.View({
19974 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19976 singleSelect: true,
19977 selectedClass: "ydataview-selected",
19981 // listen for node click?
19982 view.on("click", function(vw, index, node, e){
19983 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19987 dataModel.load("foobar.xml");
19989 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19991 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19992 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19994 * Note: old style constructor is still suported (container, template, config)
19997 * Create a new View
19998 * @param {Object} config The config object
20001 Roo.View = function(config, depreciated_tpl, depreciated_config){
20003 this.parent = false;
20005 if (typeof(depreciated_tpl) == 'undefined') {
20006 // new way.. - universal constructor.
20007 Roo.apply(this, config);
20008 this.el = Roo.get(this.el);
20011 this.el = Roo.get(config);
20012 this.tpl = depreciated_tpl;
20013 Roo.apply(this, depreciated_config);
20015 this.wrapEl = this.el.wrap().wrap();
20016 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
20019 if(typeof(this.tpl) == "string"){
20020 this.tpl = new Roo.Template(this.tpl);
20022 // support xtype ctors..
20023 this.tpl = new Roo.factory(this.tpl, Roo);
20027 this.tpl.compile();
20032 * @event beforeclick
20033 * Fires before a click is processed. Returns false to cancel the default action.
20034 * @param {Roo.View} this
20035 * @param {Number} index The index of the target node
20036 * @param {HTMLElement} node The target node
20037 * @param {Roo.EventObject} e The raw event object
20039 "beforeclick" : true,
20042 * Fires when a template node is clicked.
20043 * @param {Roo.View} this
20044 * @param {Number} index The index of the target node
20045 * @param {HTMLElement} node The target node
20046 * @param {Roo.EventObject} e The raw event object
20051 * Fires when a template node is double clicked.
20052 * @param {Roo.View} this
20053 * @param {Number} index The index of the target node
20054 * @param {HTMLElement} node The target node
20055 * @param {Roo.EventObject} e The raw event object
20059 * @event contextmenu
20060 * Fires when a template node is right clicked.
20061 * @param {Roo.View} this
20062 * @param {Number} index The index of the target node
20063 * @param {HTMLElement} node The target node
20064 * @param {Roo.EventObject} e The raw event object
20066 "contextmenu" : true,
20068 * @event selectionchange
20069 * Fires when the selected nodes change.
20070 * @param {Roo.View} this
20071 * @param {Array} selections Array of the selected nodes
20073 "selectionchange" : true,
20076 * @event beforeselect
20077 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20078 * @param {Roo.View} this
20079 * @param {HTMLElement} node The node to be selected
20080 * @param {Array} selections Array of currently selected nodes
20082 "beforeselect" : true,
20084 * @event preparedata
20085 * Fires on every row to render, to allow you to change the data.
20086 * @param {Roo.View} this
20087 * @param {Object} data to be rendered (change this)
20089 "preparedata" : true
20097 "click": this.onClick,
20098 "dblclick": this.onDblClick,
20099 "contextmenu": this.onContextMenu,
20103 this.selections = [];
20105 this.cmp = new Roo.CompositeElementLite([]);
20107 this.store = Roo.factory(this.store, Roo.data);
20108 this.setStore(this.store, true);
20111 if ( this.footer && this.footer.xtype) {
20113 var fctr = this.wrapEl.appendChild(document.createElement("div"));
20115 this.footer.dataSource = this.store;
20116 this.footer.container = fctr;
20117 this.footer = Roo.factory(this.footer, Roo);
20118 fctr.insertFirst(this.el);
20120 // this is a bit insane - as the paging toolbar seems to detach the el..
20121 // dom.parentNode.parentNode.parentNode
20122 // they get detached?
20126 Roo.View.superclass.constructor.call(this);
20131 Roo.extend(Roo.View, Roo.util.Observable, {
20134 * @cfg {Roo.data.Store} store Data store to load data from.
20139 * @cfg {String|Roo.Element} el The container element.
20144 * @cfg {String|Roo.Template} tpl The template used by this View
20148 * @cfg {String} dataName the named area of the template to use as the data area
20149 * Works with domtemplates roo-name="name"
20153 * @cfg {String} selectedClass The css class to add to selected nodes
20155 selectedClass : "x-view-selected",
20157 * @cfg {String} emptyText The empty text to show when nothing is loaded.
20162 * @cfg {String} text to display on mask (default Loading)
20166 * @cfg {Boolean} multiSelect Allow multiple selection
20168 multiSelect : false,
20170 * @cfg {Boolean} singleSelect Allow single selection
20172 singleSelect: false,
20175 * @cfg {Boolean} toggleSelect - selecting
20177 toggleSelect : false,
20180 * @cfg {Boolean} tickable - selecting
20185 * Returns the element this view is bound to.
20186 * @return {Roo.Element}
20188 getEl : function(){
20189 return this.wrapEl;
20195 * Refreshes the view. - called by datachanged on the store. - do not call directly.
20197 refresh : function(){
20198 //Roo.log('refresh');
20201 // if we are using something like 'domtemplate', then
20202 // the what gets used is:
20203 // t.applySubtemplate(NAME, data, wrapping data..)
20204 // the outer template then get' applied with
20205 // the store 'extra data'
20206 // and the body get's added to the
20207 // roo-name="data" node?
20208 // <span class='roo-tpl-{name}'></span> ?????
20212 this.clearSelections();
20213 this.el.update("");
20215 var records = this.store.getRange();
20216 if(records.length < 1) {
20218 // is this valid?? = should it render a template??
20220 this.el.update(this.emptyText);
20224 if (this.dataName) {
20225 this.el.update(t.apply(this.store.meta)); //????
20226 el = this.el.child('.roo-tpl-' + this.dataName);
20229 for(var i = 0, len = records.length; i < len; i++){
20230 var data = this.prepareData(records[i].data, i, records[i]);
20231 this.fireEvent("preparedata", this, data, i, records[i]);
20233 var d = Roo.apply({}, data);
20236 Roo.apply(d, {'roo-id' : Roo.id()});
20240 Roo.each(this.parent.item, function(item){
20241 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20244 Roo.apply(d, {'roo-data-checked' : 'checked'});
20248 html[html.length] = Roo.util.Format.trim(
20250 t.applySubtemplate(this.dataName, d, this.store.meta) :
20257 el.update(html.join(""));
20258 this.nodes = el.dom.childNodes;
20259 this.updateIndexes(0);
20264 * Function to override to reformat the data that is sent to
20265 * the template for each node.
20266 * DEPRICATED - use the preparedata event handler.
20267 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20268 * a JSON object for an UpdateManager bound view).
20270 prepareData : function(data, index, record)
20272 this.fireEvent("preparedata", this, data, index, record);
20276 onUpdate : function(ds, record){
20277 // Roo.log('on update');
20278 this.clearSelections();
20279 var index = this.store.indexOf(record);
20280 var n = this.nodes[index];
20281 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20282 n.parentNode.removeChild(n);
20283 this.updateIndexes(index, index);
20289 onAdd : function(ds, records, index)
20291 //Roo.log(['on Add', ds, records, index] );
20292 this.clearSelections();
20293 if(this.nodes.length == 0){
20297 var n = this.nodes[index];
20298 for(var i = 0, len = records.length; i < len; i++){
20299 var d = this.prepareData(records[i].data, i, records[i]);
20301 this.tpl.insertBefore(n, d);
20304 this.tpl.append(this.el, d);
20307 this.updateIndexes(index);
20310 onRemove : function(ds, record, index){
20311 // Roo.log('onRemove');
20312 this.clearSelections();
20313 var el = this.dataName ?
20314 this.el.child('.roo-tpl-' + this.dataName) :
20317 el.dom.removeChild(this.nodes[index]);
20318 this.updateIndexes(index);
20322 * Refresh an individual node.
20323 * @param {Number} index
20325 refreshNode : function(index){
20326 this.onUpdate(this.store, this.store.getAt(index));
20329 updateIndexes : function(startIndex, endIndex){
20330 var ns = this.nodes;
20331 startIndex = startIndex || 0;
20332 endIndex = endIndex || ns.length - 1;
20333 for(var i = startIndex; i <= endIndex; i++){
20334 ns[i].nodeIndex = i;
20339 * Changes the data store this view uses and refresh the view.
20340 * @param {Store} store
20342 setStore : function(store, initial){
20343 if(!initial && this.store){
20344 this.store.un("datachanged", this.refresh);
20345 this.store.un("add", this.onAdd);
20346 this.store.un("remove", this.onRemove);
20347 this.store.un("update", this.onUpdate);
20348 this.store.un("clear", this.refresh);
20349 this.store.un("beforeload", this.onBeforeLoad);
20350 this.store.un("load", this.onLoad);
20351 this.store.un("loadexception", this.onLoad);
20355 store.on("datachanged", this.refresh, this);
20356 store.on("add", this.onAdd, this);
20357 store.on("remove", this.onRemove, this);
20358 store.on("update", this.onUpdate, this);
20359 store.on("clear", this.refresh, this);
20360 store.on("beforeload", this.onBeforeLoad, this);
20361 store.on("load", this.onLoad, this);
20362 store.on("loadexception", this.onLoad, this);
20370 * onbeforeLoad - masks the loading area.
20373 onBeforeLoad : function(store,opts)
20375 //Roo.log('onBeforeLoad');
20377 this.el.update("");
20379 this.el.mask(this.mask ? this.mask : "Loading" );
20381 onLoad : function ()
20388 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20389 * @param {HTMLElement} node
20390 * @return {HTMLElement} The template node
20392 findItemFromChild : function(node){
20393 var el = this.dataName ?
20394 this.el.child('.roo-tpl-' + this.dataName,true) :
20397 if(!node || node.parentNode == el){
20400 var p = node.parentNode;
20401 while(p && p != el){
20402 if(p.parentNode == el){
20411 onClick : function(e){
20412 var item = this.findItemFromChild(e.getTarget());
20414 var index = this.indexOf(item);
20415 if(this.onItemClick(item, index, e) !== false){
20416 this.fireEvent("click", this, index, item, e);
20419 this.clearSelections();
20424 onContextMenu : function(e){
20425 var item = this.findItemFromChild(e.getTarget());
20427 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20432 onDblClick : function(e){
20433 var item = this.findItemFromChild(e.getTarget());
20435 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20439 onItemClick : function(item, index, e)
20441 if(this.fireEvent("beforeclick", this, index, item, e) === false){
20444 if (this.toggleSelect) {
20445 var m = this.isSelected(item) ? 'unselect' : 'select';
20448 _t[m](item, true, false);
20451 if(this.multiSelect || this.singleSelect){
20452 if(this.multiSelect && e.shiftKey && this.lastSelection){
20453 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20455 this.select(item, this.multiSelect && e.ctrlKey);
20456 this.lastSelection = item;
20459 if(!this.tickable){
20460 e.preventDefault();
20468 * Get the number of selected nodes.
20471 getSelectionCount : function(){
20472 return this.selections.length;
20476 * Get the currently selected nodes.
20477 * @return {Array} An array of HTMLElements
20479 getSelectedNodes : function(){
20480 return this.selections;
20484 * Get the indexes of the selected nodes.
20487 getSelectedIndexes : function(){
20488 var indexes = [], s = this.selections;
20489 for(var i = 0, len = s.length; i < len; i++){
20490 indexes.push(s[i].nodeIndex);
20496 * Clear all selections
20497 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20499 clearSelections : function(suppressEvent){
20500 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20501 this.cmp.elements = this.selections;
20502 this.cmp.removeClass(this.selectedClass);
20503 this.selections = [];
20504 if(!suppressEvent){
20505 this.fireEvent("selectionchange", this, this.selections);
20511 * Returns true if the passed node is selected
20512 * @param {HTMLElement/Number} node The node or node index
20513 * @return {Boolean}
20515 isSelected : function(node){
20516 var s = this.selections;
20520 node = this.getNode(node);
20521 return s.indexOf(node) !== -1;
20526 * @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
20527 * @param {Boolean} keepExisting (optional) true to keep existing selections
20528 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20530 select : function(nodeInfo, keepExisting, suppressEvent){
20531 if(nodeInfo instanceof Array){
20533 this.clearSelections(true);
20535 for(var i = 0, len = nodeInfo.length; i < len; i++){
20536 this.select(nodeInfo[i], true, true);
20540 var node = this.getNode(nodeInfo);
20541 if(!node || this.isSelected(node)){
20542 return; // already selected.
20545 this.clearSelections(true);
20548 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20549 Roo.fly(node).addClass(this.selectedClass);
20550 this.selections.push(node);
20551 if(!suppressEvent){
20552 this.fireEvent("selectionchange", this, this.selections);
20560 * @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
20561 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20562 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20564 unselect : function(nodeInfo, keepExisting, suppressEvent)
20566 if(nodeInfo instanceof Array){
20567 Roo.each(this.selections, function(s) {
20568 this.unselect(s, nodeInfo);
20572 var node = this.getNode(nodeInfo);
20573 if(!node || !this.isSelected(node)){
20574 //Roo.log("not selected");
20575 return; // not selected.
20579 Roo.each(this.selections, function(s) {
20581 Roo.fly(node).removeClass(this.selectedClass);
20588 this.selections= ns;
20589 this.fireEvent("selectionchange", this, this.selections);
20593 * Gets a template node.
20594 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20595 * @return {HTMLElement} The node or null if it wasn't found
20597 getNode : function(nodeInfo){
20598 if(typeof nodeInfo == "string"){
20599 return document.getElementById(nodeInfo);
20600 }else if(typeof nodeInfo == "number"){
20601 return this.nodes[nodeInfo];
20607 * Gets a range template nodes.
20608 * @param {Number} startIndex
20609 * @param {Number} endIndex
20610 * @return {Array} An array of nodes
20612 getNodes : function(start, end){
20613 var ns = this.nodes;
20614 start = start || 0;
20615 end = typeof end == "undefined" ? ns.length - 1 : end;
20618 for(var i = start; i <= end; i++){
20622 for(var i = start; i >= end; i--){
20630 * Finds the index of the passed node
20631 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20632 * @return {Number} The index of the node or -1
20634 indexOf : function(node){
20635 node = this.getNode(node);
20636 if(typeof node.nodeIndex == "number"){
20637 return node.nodeIndex;
20639 var ns = this.nodes;
20640 for(var i = 0, len = ns.length; i < len; i++){
20651 * based on jquery fullcalendar
20655 Roo.bootstrap = Roo.bootstrap || {};
20657 * @class Roo.bootstrap.Calendar
20658 * @extends Roo.bootstrap.Component
20659 * Bootstrap Calendar class
20660 * @cfg {Boolean} loadMask (true|false) default false
20661 * @cfg {Object} header generate the user specific header of the calendar, default false
20664 * Create a new Container
20665 * @param {Object} config The config object
20670 Roo.bootstrap.Calendar = function(config){
20671 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20675 * Fires when a date is selected
20676 * @param {DatePicker} this
20677 * @param {Date} date The selected date
20681 * @event monthchange
20682 * Fires when the displayed month changes
20683 * @param {DatePicker} this
20684 * @param {Date} date The selected month
20686 'monthchange': true,
20688 * @event evententer
20689 * Fires when mouse over an event
20690 * @param {Calendar} this
20691 * @param {event} Event
20693 'evententer': true,
20695 * @event eventleave
20696 * Fires when the mouse leaves an
20697 * @param {Calendar} this
20700 'eventleave': true,
20702 * @event eventclick
20703 * Fires when the mouse click an
20704 * @param {Calendar} this
20713 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20716 * @cfg {Roo.data.Store} store
20717 * The data source for the calendar
20721 * @cfg {Number} startDay
20722 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20730 getAutoCreate : function(){
20733 var fc_button = function(name, corner, style, content ) {
20734 return Roo.apply({},{
20736 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20738 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20741 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20752 style : 'width:100%',
20759 cls : 'fc-header-left',
20761 fc_button('prev', 'left', 'arrow', '‹' ),
20762 fc_button('next', 'right', 'arrow', '›' ),
20763 { tag: 'span', cls: 'fc-header-space' },
20764 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20772 cls : 'fc-header-center',
20776 cls: 'fc-header-title',
20779 html : 'month / year'
20787 cls : 'fc-header-right',
20789 /* fc_button('month', 'left', '', 'month' ),
20790 fc_button('week', '', '', 'week' ),
20791 fc_button('day', 'right', '', 'day' )
20803 header = this.header;
20806 var cal_heads = function() {
20808 // fixme - handle this.
20810 for (var i =0; i < Date.dayNames.length; i++) {
20811 var d = Date.dayNames[i];
20814 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20815 html : d.substring(0,3)
20819 ret[0].cls += ' fc-first';
20820 ret[6].cls += ' fc-last';
20823 var cal_cell = function(n) {
20826 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20831 cls: 'fc-day-number',
20835 cls: 'fc-day-content',
20839 style: 'position: relative;' // height: 17px;
20851 var cal_rows = function() {
20854 for (var r = 0; r < 6; r++) {
20861 for (var i =0; i < Date.dayNames.length; i++) {
20862 var d = Date.dayNames[i];
20863 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20866 row.cn[0].cls+=' fc-first';
20867 row.cn[0].cn[0].style = 'min-height:90px';
20868 row.cn[6].cls+=' fc-last';
20872 ret[0].cls += ' fc-first';
20873 ret[4].cls += ' fc-prev-last';
20874 ret[5].cls += ' fc-last';
20881 cls: 'fc-border-separate',
20882 style : 'width:100%',
20890 cls : 'fc-first fc-last',
20908 cls : 'fc-content',
20909 style : "position: relative;",
20912 cls : 'fc-view fc-view-month fc-grid',
20913 style : 'position: relative',
20914 unselectable : 'on',
20917 cls : 'fc-event-container',
20918 style : 'position:absolute;z-index:8;top:0;left:0;'
20936 initEvents : function()
20939 throw "can not find store for calendar";
20945 style: "text-align:center",
20949 style: "background-color:white;width:50%;margin:250 auto",
20953 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20964 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20966 var size = this.el.select('.fc-content', true).first().getSize();
20967 this.maskEl.setSize(size.width, size.height);
20968 this.maskEl.enableDisplayMode("block");
20969 if(!this.loadMask){
20970 this.maskEl.hide();
20973 this.store = Roo.factory(this.store, Roo.data);
20974 this.store.on('load', this.onLoad, this);
20975 this.store.on('beforeload', this.onBeforeLoad, this);
20979 this.cells = this.el.select('.fc-day',true);
20980 //Roo.log(this.cells);
20981 this.textNodes = this.el.query('.fc-day-number');
20982 this.cells.addClassOnOver('fc-state-hover');
20984 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20985 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20986 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20987 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20989 this.on('monthchange', this.onMonthChange, this);
20991 this.update(new Date().clearTime());
20994 resize : function() {
20995 var sz = this.el.getSize();
20997 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20998 this.el.select('.fc-day-content div',true).setHeight(34);
21003 showPrevMonth : function(e){
21004 this.update(this.activeDate.add("mo", -1));
21006 showToday : function(e){
21007 this.update(new Date().clearTime());
21010 showNextMonth : function(e){
21011 this.update(this.activeDate.add("mo", 1));
21015 showPrevYear : function(){
21016 this.update(this.activeDate.add("y", -1));
21020 showNextYear : function(){
21021 this.update(this.activeDate.add("y", 1));
21026 update : function(date)
21028 var vd = this.activeDate;
21029 this.activeDate = date;
21030 // if(vd && this.el){
21031 // var t = date.getTime();
21032 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21033 // Roo.log('using add remove');
21035 // this.fireEvent('monthchange', this, date);
21037 // this.cells.removeClass("fc-state-highlight");
21038 // this.cells.each(function(c){
21039 // if(c.dateValue == t){
21040 // c.addClass("fc-state-highlight");
21041 // setTimeout(function(){
21042 // try{c.dom.firstChild.focus();}catch(e){}
21052 var days = date.getDaysInMonth();
21054 var firstOfMonth = date.getFirstDateOfMonth();
21055 var startingPos = firstOfMonth.getDay()-this.startDay;
21057 if(startingPos < this.startDay){
21061 var pm = date.add(Date.MONTH, -1);
21062 var prevStart = pm.getDaysInMonth()-startingPos;
21064 this.cells = this.el.select('.fc-day',true);
21065 this.textNodes = this.el.query('.fc-day-number');
21066 this.cells.addClassOnOver('fc-state-hover');
21068 var cells = this.cells.elements;
21069 var textEls = this.textNodes;
21071 Roo.each(cells, function(cell){
21072 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21075 days += startingPos;
21077 // convert everything to numbers so it's fast
21078 var day = 86400000;
21079 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21082 //Roo.log(prevStart);
21084 var today = new Date().clearTime().getTime();
21085 var sel = date.clearTime().getTime();
21086 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21087 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21088 var ddMatch = this.disabledDatesRE;
21089 var ddText = this.disabledDatesText;
21090 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21091 var ddaysText = this.disabledDaysText;
21092 var format = this.format;
21094 var setCellClass = function(cal, cell){
21098 //Roo.log('set Cell Class');
21100 var t = d.getTime();
21104 cell.dateValue = t;
21106 cell.className += " fc-today";
21107 cell.className += " fc-state-highlight";
21108 cell.title = cal.todayText;
21111 // disable highlight in other month..
21112 //cell.className += " fc-state-highlight";
21117 cell.className = " fc-state-disabled";
21118 cell.title = cal.minText;
21122 cell.className = " fc-state-disabled";
21123 cell.title = cal.maxText;
21127 if(ddays.indexOf(d.getDay()) != -1){
21128 cell.title = ddaysText;
21129 cell.className = " fc-state-disabled";
21132 if(ddMatch && format){
21133 var fvalue = d.dateFormat(format);
21134 if(ddMatch.test(fvalue)){
21135 cell.title = ddText.replace("%0", fvalue);
21136 cell.className = " fc-state-disabled";
21140 if (!cell.initialClassName) {
21141 cell.initialClassName = cell.dom.className;
21144 cell.dom.className = cell.initialClassName + ' ' + cell.className;
21149 for(; i < startingPos; i++) {
21150 textEls[i].innerHTML = (++prevStart);
21151 d.setDate(d.getDate()+1);
21153 cells[i].className = "fc-past fc-other-month";
21154 setCellClass(this, cells[i]);
21159 for(; i < days; i++){
21160 intDay = i - startingPos + 1;
21161 textEls[i].innerHTML = (intDay);
21162 d.setDate(d.getDate()+1);
21164 cells[i].className = ''; // "x-date-active";
21165 setCellClass(this, cells[i]);
21169 for(; i < 42; i++) {
21170 textEls[i].innerHTML = (++extraDays);
21171 d.setDate(d.getDate()+1);
21173 cells[i].className = "fc-future fc-other-month";
21174 setCellClass(this, cells[i]);
21177 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21179 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21181 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21182 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21184 if(totalRows != 6){
21185 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21186 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21189 this.fireEvent('monthchange', this, date);
21193 if(!this.internalRender){
21194 var main = this.el.dom.firstChild;
21195 var w = main.offsetWidth;
21196 this.el.setWidth(w + this.el.getBorderWidth("lr"));
21197 Roo.fly(main).setWidth(w);
21198 this.internalRender = true;
21199 // opera does not respect the auto grow header center column
21200 // then, after it gets a width opera refuses to recalculate
21201 // without a second pass
21202 if(Roo.isOpera && !this.secondPass){
21203 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21204 this.secondPass = true;
21205 this.update.defer(10, this, [date]);
21212 findCell : function(dt) {
21213 dt = dt.clearTime().getTime();
21215 this.cells.each(function(c){
21216 //Roo.log("check " +c.dateValue + '?=' + dt);
21217 if(c.dateValue == dt){
21227 findCells : function(ev) {
21228 var s = ev.start.clone().clearTime().getTime();
21230 var e= ev.end.clone().clearTime().getTime();
21233 this.cells.each(function(c){
21234 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21236 if(c.dateValue > e){
21239 if(c.dateValue < s){
21248 // findBestRow: function(cells)
21252 // for (var i =0 ; i < cells.length;i++) {
21253 // ret = Math.max(cells[i].rows || 0,ret);
21260 addItem : function(ev)
21262 // look for vertical location slot in
21263 var cells = this.findCells(ev);
21265 // ev.row = this.findBestRow(cells);
21267 // work out the location.
21271 for(var i =0; i < cells.length; i++) {
21273 cells[i].row = cells[0].row;
21276 cells[i].row = cells[i].row + 1;
21286 if (crow.start.getY() == cells[i].getY()) {
21288 crow.end = cells[i];
21305 cells[0].events.push(ev);
21307 this.calevents.push(ev);
21310 clearEvents: function() {
21312 if(!this.calevents){
21316 Roo.each(this.cells.elements, function(c){
21322 Roo.each(this.calevents, function(e) {
21323 Roo.each(e.els, function(el) {
21324 el.un('mouseenter' ,this.onEventEnter, this);
21325 el.un('mouseleave' ,this.onEventLeave, this);
21330 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21336 renderEvents: function()
21340 this.cells.each(function(c) {
21349 if(c.row != c.events.length){
21350 r = 4 - (4 - (c.row - c.events.length));
21353 c.events = ev.slice(0, r);
21354 c.more = ev.slice(r);
21356 if(c.more.length && c.more.length == 1){
21357 c.events.push(c.more.pop());
21360 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21364 this.cells.each(function(c) {
21366 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21369 for (var e = 0; e < c.events.length; e++){
21370 var ev = c.events[e];
21371 var rows = ev.rows;
21373 for(var i = 0; i < rows.length; i++) {
21375 // how many rows should it span..
21378 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21379 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21381 unselectable : "on",
21384 cls: 'fc-event-inner',
21388 // cls: 'fc-event-time',
21389 // html : cells.length > 1 ? '' : ev.time
21393 cls: 'fc-event-title',
21394 html : String.format('{0}', ev.title)
21401 cls: 'ui-resizable-handle ui-resizable-e',
21402 html : '  '
21409 cfg.cls += ' fc-event-start';
21411 if ((i+1) == rows.length) {
21412 cfg.cls += ' fc-event-end';
21415 var ctr = _this.el.select('.fc-event-container',true).first();
21416 var cg = ctr.createChild(cfg);
21418 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21419 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21421 var r = (c.more.length) ? 1 : 0;
21422 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
21423 cg.setWidth(ebox.right - sbox.x -2);
21425 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21426 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21427 cg.on('click', _this.onEventClick, _this, ev);
21438 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21439 style : 'position: absolute',
21440 unselectable : "on",
21443 cls: 'fc-event-inner',
21447 cls: 'fc-event-title',
21455 cls: 'ui-resizable-handle ui-resizable-e',
21456 html : '  '
21462 var ctr = _this.el.select('.fc-event-container',true).first();
21463 var cg = ctr.createChild(cfg);
21465 var sbox = c.select('.fc-day-content',true).first().getBox();
21466 var ebox = c.select('.fc-day-content',true).first().getBox();
21468 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
21469 cg.setWidth(ebox.right - sbox.x -2);
21471 cg.on('click', _this.onMoreEventClick, _this, c.more);
21481 onEventEnter: function (e, el,event,d) {
21482 this.fireEvent('evententer', this, el, event);
21485 onEventLeave: function (e, el,event,d) {
21486 this.fireEvent('eventleave', this, el, event);
21489 onEventClick: function (e, el,event,d) {
21490 this.fireEvent('eventclick', this, el, event);
21493 onMonthChange: function () {
21497 onMoreEventClick: function(e, el, more)
21501 this.calpopover.placement = 'right';
21502 this.calpopover.setTitle('More');
21504 this.calpopover.setContent('');
21506 var ctr = this.calpopover.el.select('.popover-content', true).first();
21508 Roo.each(more, function(m){
21510 cls : 'fc-event-hori fc-event-draggable',
21513 var cg = ctr.createChild(cfg);
21515 cg.on('click', _this.onEventClick, _this, m);
21518 this.calpopover.show(el);
21523 onLoad: function ()
21525 this.calevents = [];
21528 if(this.store.getCount() > 0){
21529 this.store.data.each(function(d){
21532 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21533 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21534 time : d.data.start_time,
21535 title : d.data.title,
21536 description : d.data.description,
21537 venue : d.data.venue
21542 this.renderEvents();
21544 if(this.calevents.length && this.loadMask){
21545 this.maskEl.hide();
21549 onBeforeLoad: function()
21551 this.clearEvents();
21553 this.maskEl.show();
21567 * @class Roo.bootstrap.Popover
21568 * @extends Roo.bootstrap.Component
21569 * @parent none builder
21570 * @children Roo.bootstrap.Component
21571 * Bootstrap Popover class
21572 * @cfg {String} html contents of the popover (or false to use children..)
21573 * @cfg {String} title of popover (or false to hide)
21574 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21575 * @cfg {String} trigger click || hover (or false to trigger manually)
21576 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21577 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21578 * - if false and it has a 'parent' then it will be automatically added to that element
21579 * - if string - Roo.get will be called
21580 * @cfg {Number} delay - delay before showing
21583 * Create a new Popover
21584 * @param {Object} config The config object
21587 Roo.bootstrap.Popover = function(config){
21588 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21594 * After the popover show
21596 * @param {Roo.bootstrap.Popover} this
21601 * After the popover hide
21603 * @param {Roo.bootstrap.Popover} this
21609 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21614 placement : 'right',
21615 trigger : 'hover', // hover
21621 can_build_overlaid : false,
21623 maskEl : false, // the mask element
21626 alignEl : false, // when show is called with an element - this get's stored.
21628 getChildContainer : function()
21630 return this.contentEl;
21633 getPopoverHeader : function()
21635 this.title = true; // flag not to hide it..
21636 this.headerEl.addClass('p-0');
21637 return this.headerEl
21641 getAutoCreate : function(){
21644 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21645 style: 'display:block',
21651 cls : 'popover-inner ',
21655 cls: 'popover-title popover-header',
21656 html : this.title === false ? '' : this.title
21659 cls : 'popover-content popover-body ' + (this.cls || ''),
21660 html : this.html || ''
21671 * @param {string} the title
21673 setTitle: function(str)
21677 this.headerEl.dom.innerHTML = str;
21682 * @param {string} the body content
21684 setContent: function(str)
21687 if (this.contentEl) {
21688 this.contentEl.dom.innerHTML = str;
21692 // as it get's added to the bottom of the page.
21693 onRender : function(ct, position)
21695 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21700 var cfg = Roo.apply({}, this.getAutoCreate());
21704 cfg.cls += ' ' + this.cls;
21707 cfg.style = this.style;
21709 //Roo.log("adding to ");
21710 this.el = Roo.get(document.body).createChild(cfg, position);
21711 // Roo.log(this.el);
21714 this.contentEl = this.el.select('.popover-content',true).first();
21715 this.headerEl = this.el.select('.popover-title',true).first();
21718 if(typeof(this.items) != 'undefined'){
21719 var items = this.items;
21722 for(var i =0;i < items.length;i++) {
21723 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21727 this.items = nitems;
21729 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21730 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21737 resizeMask : function()
21739 this.maskEl.setSize(
21740 Roo.lib.Dom.getViewWidth(true),
21741 Roo.lib.Dom.getViewHeight(true)
21745 initEvents : function()
21749 Roo.bootstrap.Popover.register(this);
21752 this.arrowEl = this.el.select('.arrow',true).first();
21753 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21754 this.el.enableDisplayMode('block');
21758 if (this.over === false && !this.parent()) {
21761 if (this.triggers === false) {
21766 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21767 var triggers = this.trigger ? this.trigger.split(' ') : [];
21768 Roo.each(triggers, function(trigger) {
21770 if (trigger == 'click') {
21771 on_el.on('click', this.toggle, this);
21772 } else if (trigger != 'manual') {
21773 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21774 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21776 on_el.on(eventIn ,this.enter, this);
21777 on_el.on(eventOut, this.leave, this);
21787 toggle : function () {
21788 this.hoverState == 'in' ? this.leave() : this.enter();
21791 enter : function () {
21793 clearTimeout(this.timeout);
21795 this.hoverState = 'in';
21797 if (!this.delay || !this.delay.show) {
21802 this.timeout = setTimeout(function () {
21803 if (_t.hoverState == 'in') {
21806 }, this.delay.show)
21809 leave : function() {
21810 clearTimeout(this.timeout);
21812 this.hoverState = 'out';
21814 if (!this.delay || !this.delay.hide) {
21819 this.timeout = setTimeout(function () {
21820 if (_t.hoverState == 'out') {
21823 }, this.delay.hide)
21827 * update the position of the dialog
21828 * normally this is needed if the popover get's bigger - due to a Table reload etc..
21833 doAlign : function()
21836 if (this.alignEl) {
21837 this.updatePosition(this.placement, true);
21840 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21841 var es = this.el.getSize();
21842 var x = Roo.lib.Dom.getViewWidth()/2;
21843 var y = Roo.lib.Dom.getViewHeight()/2;
21844 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21856 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21857 * @param {string} (left|right|top|bottom) position
21859 show : function (on_el, placement)
21861 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21862 on_el = on_el || false; // default to false
21865 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21866 on_el = this.parent().el;
21867 } else if (this.over) {
21868 on_el = Roo.get(this.over);
21873 this.alignEl = Roo.get( on_el );
21876 this.render(document.body);
21882 if (this.title === false) {
21883 this.headerEl.hide();
21888 this.el.dom.style.display = 'block';
21892 //var arrow = this.el.select('.arrow',true).first();
21893 //arrow.set(align[2],
21895 this.el.addClass('in');
21899 this.hoverState = 'in';
21902 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21903 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21904 this.maskEl.dom.style.display = 'block';
21905 this.maskEl.addClass('show');
21907 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21909 this.fireEvent('show', this);
21913 * fire this manually after loading a grid in the table for example
21914 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21915 * @param {Boolean} try and move it if we cant get right position.
21917 updatePosition : function(placement, try_move)
21919 // allow for calling with no parameters
21920 placement = placement ? placement : this.placement;
21921 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21923 this.el.removeClass([
21924 'fade','top','bottom', 'left', 'right','in',
21925 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21927 this.el.addClass(placement + ' bs-popover-' + placement);
21929 if (!this.alignEl ) {
21933 switch (placement) {
21935 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21936 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21937 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21938 //normal display... or moved up/down.
21939 this.el.setXY(offset);
21940 var xy = this.alignEl.getAnchorXY('tr', false);
21942 this.arrowEl.setXY(xy);
21945 // continue through...
21946 return this.updatePosition('left', false);
21950 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21951 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21952 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21953 //normal display... or moved up/down.
21954 this.el.setXY(offset);
21955 var xy = this.alignEl.getAnchorXY('tl', false);
21956 xy[0]-=10;xy[1]+=5; // << fix me
21957 this.arrowEl.setXY(xy);
21961 return this.updatePosition('right', false);
21964 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21965 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21966 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21967 //normal display... or moved up/down.
21968 this.el.setXY(offset);
21969 var xy = this.alignEl.getAnchorXY('t', false);
21970 xy[1]-=10; // << fix me
21971 this.arrowEl.setXY(xy);
21975 return this.updatePosition('bottom', false);
21978 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21979 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21980 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21981 //normal display... or moved up/down.
21982 this.el.setXY(offset);
21983 var xy = this.alignEl.getAnchorXY('b', false);
21984 xy[1]+=2; // << fix me
21985 this.arrowEl.setXY(xy);
21989 return this.updatePosition('top', false);
22000 this.el.setXY([0,0]);
22001 this.el.removeClass('in');
22003 this.hoverState = null;
22004 this.maskEl.hide(); // always..
22005 this.fireEvent('hide', this);
22011 Roo.apply(Roo.bootstrap.Popover, {
22014 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
22015 'right' : ['l-br', [10,0], 'right bs-popover-right'],
22016 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
22017 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
22022 clickHander : false,
22026 onMouseDown : function(e)
22028 if (this.popups.length && !e.getTarget(".roo-popover")) {
22029 /// what is nothing is showing..
22038 register : function(popup)
22040 if (!Roo.bootstrap.Popover.clickHandler) {
22041 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22043 // hide other popups.
22044 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
22045 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
22046 this.hideAll(); //<< why?
22047 //this.popups.push(popup);
22049 hideAll : function()
22051 this.popups.forEach(function(p) {
22055 onShow : function() {
22056 Roo.bootstrap.Popover.popups.push(this);
22058 onHide : function() {
22059 Roo.bootstrap.Popover.popups.remove(this);
22064 * @class Roo.bootstrap.PopoverNav
22065 * @extends Roo.bootstrap.nav.Simplebar
22066 * @parent Roo.bootstrap.Popover
22067 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22069 * Bootstrap Popover header navigation class
22070 * FIXME? should this go under nav?
22074 * Create a new Popover Header Navigation
22075 * @param {Object} config The config object
22078 Roo.bootstrap.PopoverNav = function(config){
22079 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22082 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar, {
22085 container_method : 'getPopoverHeader'
22103 * @class Roo.bootstrap.Progress
22104 * @extends Roo.bootstrap.Component
22105 * @children Roo.bootstrap.ProgressBar
22106 * Bootstrap Progress class
22107 * @cfg {Boolean} striped striped of the progress bar
22108 * @cfg {Boolean} active animated of the progress bar
22112 * Create a new Progress
22113 * @param {Object} config The config object
22116 Roo.bootstrap.Progress = function(config){
22117 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22120 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
22125 getAutoCreate : function(){
22133 cfg.cls += ' progress-striped';
22137 cfg.cls += ' active';
22156 * @class Roo.bootstrap.ProgressBar
22157 * @extends Roo.bootstrap.Component
22158 * Bootstrap ProgressBar class
22159 * @cfg {Number} aria_valuenow aria-value now
22160 * @cfg {Number} aria_valuemin aria-value min
22161 * @cfg {Number} aria_valuemax aria-value max
22162 * @cfg {String} label label for the progress bar
22163 * @cfg {String} panel (success | info | warning | danger )
22164 * @cfg {String} role role of the progress bar
22165 * @cfg {String} sr_only text
22169 * Create a new ProgressBar
22170 * @param {Object} config The config object
22173 Roo.bootstrap.ProgressBar = function(config){
22174 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22177 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
22181 aria_valuemax : 100,
22187 getAutoCreate : function()
22192 cls: 'progress-bar',
22193 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22205 cfg.role = this.role;
22208 if(this.aria_valuenow){
22209 cfg['aria-valuenow'] = this.aria_valuenow;
22212 if(this.aria_valuemin){
22213 cfg['aria-valuemin'] = this.aria_valuemin;
22216 if(this.aria_valuemax){
22217 cfg['aria-valuemax'] = this.aria_valuemax;
22220 if(this.label && !this.sr_only){
22221 cfg.html = this.label;
22225 cfg.cls += ' progress-bar-' + this.panel;
22231 update : function(aria_valuenow)
22233 this.aria_valuenow = aria_valuenow;
22235 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22243 * @class Roo.bootstrap.TabGroup
22244 * @extends Roo.bootstrap.Column
22245 * @children Roo.bootstrap.TabPanel
22246 * Bootstrap Column class
22247 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22248 * @cfg {Boolean} carousel true to make the group behave like a carousel
22249 * @cfg {Boolean} bullets show bullets for the panels
22250 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22251 * @cfg {Number} timer auto slide timer .. default 0 millisecond
22252 * @cfg {Boolean} showarrow (true|false) show arrow default true
22255 * Create a new TabGroup
22256 * @param {Object} config The config object
22259 Roo.bootstrap.TabGroup = function(config){
22260 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22262 this.navId = Roo.id();
22265 Roo.bootstrap.TabGroup.register(this);
22269 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
22272 transition : false,
22277 slideOnTouch : false,
22280 getAutoCreate : function()
22282 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22284 cfg.cls += ' tab-content';
22286 if (this.carousel) {
22287 cfg.cls += ' carousel slide';
22290 cls : 'carousel-inner',
22294 if(this.bullets && !Roo.isTouch){
22297 cls : 'carousel-bullets',
22301 if(this.bullets_cls){
22302 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22309 cfg.cn[0].cn.push(bullets);
22312 if(this.showarrow){
22313 cfg.cn[0].cn.push({
22315 class : 'carousel-arrow',
22319 class : 'carousel-prev',
22323 class : 'fa fa-chevron-left'
22329 class : 'carousel-next',
22333 class : 'fa fa-chevron-right'
22346 initEvents: function()
22348 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22349 // this.el.on("touchstart", this.onTouchStart, this);
22352 if(this.autoslide){
22355 this.slideFn = window.setInterval(function() {
22356 _this.showPanelNext();
22360 if(this.showarrow){
22361 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22362 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22368 // onTouchStart : function(e, el, o)
22370 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22374 // this.showPanelNext();
22378 getChildContainer : function()
22380 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22384 * register a Navigation item
22385 * @param {Roo.bootstrap.nav.Item} the navitem to add
22387 register : function(item)
22389 this.tabs.push( item);
22390 item.navId = this.navId; // not really needed..
22395 getActivePanel : function()
22398 Roo.each(this.tabs, function(t) {
22408 getPanelByName : function(n)
22411 Roo.each(this.tabs, function(t) {
22412 if (t.tabId == n) {
22420 indexOfPanel : function(p)
22423 Roo.each(this.tabs, function(t,i) {
22424 if (t.tabId == p.tabId) {
22433 * show a specific panel
22434 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22435 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22437 showPanel : function (pan)
22439 if(this.transition || typeof(pan) == 'undefined'){
22440 Roo.log("waiting for the transitionend");
22444 if (typeof(pan) == 'number') {
22445 pan = this.tabs[pan];
22448 if (typeof(pan) == 'string') {
22449 pan = this.getPanelByName(pan);
22452 var cur = this.getActivePanel();
22455 Roo.log('pan or acitve pan is undefined');
22459 if (pan.tabId == this.getActivePanel().tabId) {
22463 if (false === cur.fireEvent('beforedeactivate')) {
22467 if(this.bullets > 0 && !Roo.isTouch){
22468 this.setActiveBullet(this.indexOfPanel(pan));
22471 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22473 //class="carousel-item carousel-item-next carousel-item-left"
22475 this.transition = true;
22476 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
22477 var lr = dir == 'next' ? 'left' : 'right';
22478 pan.el.addClass(dir); // or prev
22479 pan.el.addClass('carousel-item-' + dir); // or prev
22480 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22481 cur.el.addClass(lr); // or right
22482 pan.el.addClass(lr);
22483 cur.el.addClass('carousel-item-' +lr); // or right
22484 pan.el.addClass('carousel-item-' +lr);
22488 cur.el.on('transitionend', function() {
22489 Roo.log("trans end?");
22491 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22492 pan.setActive(true);
22494 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22495 cur.setActive(false);
22497 _this.transition = false;
22499 }, this, { single: true } );
22504 cur.setActive(false);
22505 pan.setActive(true);
22510 showPanelNext : function()
22512 var i = this.indexOfPanel(this.getActivePanel());
22514 if (i >= this.tabs.length - 1 && !this.autoslide) {
22518 if (i >= this.tabs.length - 1 && this.autoslide) {
22522 this.showPanel(this.tabs[i+1]);
22525 showPanelPrev : function()
22527 var i = this.indexOfPanel(this.getActivePanel());
22529 if (i < 1 && !this.autoslide) {
22533 if (i < 1 && this.autoslide) {
22534 i = this.tabs.length;
22537 this.showPanel(this.tabs[i-1]);
22541 addBullet: function()
22543 if(!this.bullets || Roo.isTouch){
22546 var ctr = this.el.select('.carousel-bullets',true).first();
22547 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22548 var bullet = ctr.createChild({
22549 cls : 'bullet bullet-' + i
22550 },ctr.dom.lastChild);
22555 bullet.on('click', (function(e, el, o, ii, t){
22557 e.preventDefault();
22559 this.showPanel(ii);
22561 if(this.autoslide && this.slideFn){
22562 clearInterval(this.slideFn);
22563 this.slideFn = window.setInterval(function() {
22564 _this.showPanelNext();
22568 }).createDelegate(this, [i, bullet], true));
22573 setActiveBullet : function(i)
22579 Roo.each(this.el.select('.bullet', true).elements, function(el){
22580 el.removeClass('selected');
22583 var bullet = this.el.select('.bullet-' + i, true).first();
22589 bullet.addClass('selected');
22600 Roo.apply(Roo.bootstrap.TabGroup, {
22604 * register a Navigation Group
22605 * @param {Roo.bootstrap.nav.Group} the navgroup to add
22607 register : function(navgrp)
22609 this.groups[navgrp.navId] = navgrp;
22613 * fetch a Navigation Group based on the navigation ID
22614 * if one does not exist , it will get created.
22615 * @param {string} the navgroup to add
22616 * @returns {Roo.bootstrap.nav.Group} the navgroup
22618 get: function(navId) {
22619 if (typeof(this.groups[navId]) == 'undefined') {
22620 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22622 return this.groups[navId] ;
22637 * @class Roo.bootstrap.TabPanel
22638 * @extends Roo.bootstrap.Component
22639 * @children Roo.bootstrap.Component
22640 * Bootstrap TabPanel class
22641 * @cfg {Boolean} active panel active
22642 * @cfg {String} html panel content
22643 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22644 * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22645 * @cfg {String} href click to link..
22646 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22650 * Create a new TabPanel
22651 * @param {Object} config The config object
22654 Roo.bootstrap.TabPanel = function(config){
22655 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22659 * Fires when the active status changes
22660 * @param {Roo.bootstrap.TabPanel} this
22661 * @param {Boolean} state the new state
22666 * @event beforedeactivate
22667 * Fires before a tab is de-activated - can be used to do validation on a form.
22668 * @param {Roo.bootstrap.TabPanel} this
22669 * @return {Boolean} false if there is an error
22672 'beforedeactivate': true
22675 this.tabId = this.tabId || Roo.id();
22679 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22686 touchSlide : false,
22687 getAutoCreate : function(){
22692 // item is needed for carousel - not sure if it has any effect otherwise
22693 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22694 html: this.html || ''
22698 cfg.cls += ' active';
22702 cfg.tabId = this.tabId;
22710 initEvents: function()
22712 var p = this.parent();
22714 this.navId = this.navId || p.navId;
22716 if (typeof(this.navId) != 'undefined') {
22717 // not really needed.. but just in case.. parent should be a NavGroup.
22718 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22722 var i = tg.tabs.length - 1;
22724 if(this.active && tg.bullets > 0 && i < tg.bullets){
22725 tg.setActiveBullet(i);
22729 this.el.on('click', this.onClick, this);
22731 if(Roo.isTouch && this.touchSlide){
22732 this.el.on("touchstart", this.onTouchStart, this);
22733 this.el.on("touchmove", this.onTouchMove, this);
22734 this.el.on("touchend", this.onTouchEnd, this);
22739 onRender : function(ct, position)
22741 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22744 setActive : function(state)
22746 Roo.log("panel - set active " + this.tabId + "=" + state);
22748 this.active = state;
22750 this.el.removeClass('active');
22752 } else if (!this.el.hasClass('active')) {
22753 this.el.addClass('active');
22756 this.fireEvent('changed', this, state);
22759 onClick : function(e)
22761 e.preventDefault();
22763 if(!this.href.length){
22767 window.location.href = this.href;
22776 onTouchStart : function(e)
22778 this.swiping = false;
22780 this.startX = e.browserEvent.touches[0].clientX;
22781 this.startY = e.browserEvent.touches[0].clientY;
22784 onTouchMove : function(e)
22786 this.swiping = true;
22788 this.endX = e.browserEvent.touches[0].clientX;
22789 this.endY = e.browserEvent.touches[0].clientY;
22792 onTouchEnd : function(e)
22799 var tabGroup = this.parent();
22801 if(this.endX > this.startX){ // swiping right
22802 tabGroup.showPanelPrev();
22806 if(this.startX > this.endX){ // swiping left
22807 tabGroup.showPanelNext();
22826 * @class Roo.bootstrap.form.DateField
22827 * @extends Roo.bootstrap.form.Input
22828 * Bootstrap DateField class
22829 * @cfg {Number} weekStart default 0
22830 * @cfg {String} viewMode default empty, (months|years)
22831 * @cfg {String} minViewMode default empty, (months|years)
22832 * @cfg {Number} startDate default -Infinity
22833 * @cfg {Number} endDate default Infinity
22834 * @cfg {Boolean} todayHighlight default false
22835 * @cfg {Boolean} todayBtn default false
22836 * @cfg {Boolean} calendarWeeks default false
22837 * @cfg {Object} daysOfWeekDisabled default empty
22838 * @cfg {Boolean} singleMode default false (true | false)
22840 * @cfg {Boolean} keyboardNavigation default true
22841 * @cfg {String} language default en
22844 * Create a new DateField
22845 * @param {Object} config The config object
22848 Roo.bootstrap.form.DateField = function(config){
22849 Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22853 * Fires when this field show.
22854 * @param {Roo.bootstrap.form.DateField} this
22855 * @param {Mixed} date The date value
22860 * Fires when this field hide.
22861 * @param {Roo.bootstrap.form.DateField} this
22862 * @param {Mixed} date The date value
22867 * Fires when select a date.
22868 * @param {Roo.bootstrap.form.DateField} this
22869 * @param {Mixed} date The date value
22873 * @event beforeselect
22874 * Fires when before select a date.
22875 * @param {Roo.bootstrap.form.DateField} this
22876 * @param {Mixed} date The date value
22878 beforeselect : true
22882 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input, {
22885 * @cfg {String} format
22886 * The default date format string which can be overriden for localization support. The format must be
22887 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22891 * @cfg {String} altFormats
22892 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22893 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22895 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22903 todayHighlight : false,
22909 keyboardNavigation: true,
22911 calendarWeeks: false,
22913 startDate: -Infinity,
22917 daysOfWeekDisabled: [],
22921 singleMode : false,
22923 UTCDate: function()
22925 return new Date(Date.UTC.apply(Date, arguments));
22928 UTCToday: function()
22930 var today = new Date();
22931 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22934 getDate: function() {
22935 var d = this.getUTCDate();
22936 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22939 getUTCDate: function() {
22943 setDate: function(d) {
22944 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22947 setUTCDate: function(d) {
22949 this.setValue(this.formatDate(this.date));
22952 onRender: function(ct, position)
22955 Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22957 this.language = this.language || 'en';
22958 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22959 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22961 this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22962 this.format = this.format || 'm/d/y';
22963 this.isInline = false;
22964 this.isInput = true;
22965 this.component = this.el.select('.add-on', true).first() || false;
22966 this.component = (this.component && this.component.length === 0) ? false : this.component;
22967 this.hasInput = this.component && this.inputEl().length;
22969 if (typeof(this.minViewMode === 'string')) {
22970 switch (this.minViewMode) {
22972 this.minViewMode = 1;
22975 this.minViewMode = 2;
22978 this.minViewMode = 0;
22983 if (typeof(this.viewMode === 'string')) {
22984 switch (this.viewMode) {
22997 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22999 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
23001 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23003 this.picker().on('mousedown', this.onMousedown, this);
23004 this.picker().on('click', this.onClick, this);
23006 this.picker().addClass('datepicker-dropdown');
23008 this.startViewMode = this.viewMode;
23010 if(this.singleMode){
23011 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
23012 v.setVisibilityMode(Roo.Element.DISPLAY);
23016 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
23017 v.setStyle('width', '189px');
23021 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
23022 if(!this.calendarWeeks){
23027 v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23028 v.attr('colspan', function(i, val){
23029 return parseInt(val) + 1;
23034 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23036 this.setStartDate(this.startDate);
23037 this.setEndDate(this.endDate);
23039 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23046 if(this.isInline) {
23051 picker : function()
23053 return this.pickerEl;
23054 // return this.el.select('.datepicker', true).first();
23057 fillDow: function()
23059 var dowCnt = this.weekStart;
23068 if(this.calendarWeeks){
23076 while (dowCnt < this.weekStart + 7) {
23080 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23084 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23087 fillMonths: function()
23090 var months = this.picker().select('>.datepicker-months td', true).first();
23092 months.dom.innerHTML = '';
23098 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23101 months.createChild(month);
23108 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;
23110 if (this.date < this.startDate) {
23111 this.viewDate = new Date(this.startDate);
23112 } else if (this.date > this.endDate) {
23113 this.viewDate = new Date(this.endDate);
23115 this.viewDate = new Date(this.date);
23123 var d = new Date(this.viewDate),
23124 year = d.getUTCFullYear(),
23125 month = d.getUTCMonth(),
23126 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23127 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23128 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23129 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23130 currentDate = this.date && this.date.valueOf(),
23131 today = this.UTCToday();
23133 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23135 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23137 // this.picker.select('>tfoot th.today').
23138 // .text(dates[this.language].today)
23139 // .toggle(this.todayBtn !== false);
23141 this.updateNavArrows();
23144 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23146 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23148 prevMonth.setUTCDate(day);
23150 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23152 var nextMonth = new Date(prevMonth);
23154 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23156 nextMonth = nextMonth.valueOf();
23158 var fillMonths = false;
23160 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23162 while(prevMonth.valueOf() <= nextMonth) {
23165 if (prevMonth.getUTCDay() === this.weekStart) {
23167 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23175 if(this.calendarWeeks){
23176 // ISO 8601: First week contains first thursday.
23177 // ISO also states week starts on Monday, but we can be more abstract here.
23179 // Start of current week: based on weekstart/current date
23180 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23181 // Thursday of this week
23182 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23183 // First Thursday of year, year from thursday
23184 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23185 // Calendar week: ms between thursdays, div ms per day, div 7 days
23186 calWeek = (th - yth) / 864e5 / 7 + 1;
23188 fillMonths.cn.push({
23196 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23198 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23201 if (this.todayHighlight &&
23202 prevMonth.getUTCFullYear() == today.getFullYear() &&
23203 prevMonth.getUTCMonth() == today.getMonth() &&
23204 prevMonth.getUTCDate() == today.getDate()) {
23205 clsName += ' today';
23208 if (currentDate && prevMonth.valueOf() === currentDate) {
23209 clsName += ' active';
23212 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23213 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23214 clsName += ' disabled';
23217 fillMonths.cn.push({
23219 cls: 'day ' + clsName,
23220 html: prevMonth.getDate()
23223 prevMonth.setDate(prevMonth.getDate()+1);
23226 var currentYear = this.date && this.date.getUTCFullYear();
23227 var currentMonth = this.date && this.date.getUTCMonth();
23229 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23231 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23232 v.removeClass('active');
23234 if(currentYear === year && k === currentMonth){
23235 v.addClass('active');
23238 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23239 v.addClass('disabled');
23245 year = parseInt(year/10, 10) * 10;
23247 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23249 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23252 for (var i = -1; i < 11; i++) {
23253 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23255 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23263 showMode: function(dir)
23266 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23269 Roo.each(this.picker().select('>div',true).elements, function(v){
23270 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23273 this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23278 if(this.isInline) {
23282 this.picker().removeClass(['bottom', 'top']);
23284 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23286 * place to the top of element!
23290 this.picker().addClass('top');
23291 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23296 this.picker().addClass('bottom');
23298 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23301 parseDate : function(value)
23303 if(!value || value instanceof Date){
23306 var v = Date.parseDate(value, this.format);
23307 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23308 v = Date.parseDate(value, 'Y-m-d');
23310 if(!v && this.altFormats){
23311 if(!this.altFormatsArray){
23312 this.altFormatsArray = this.altFormats.split("|");
23314 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23315 v = Date.parseDate(value, this.altFormatsArray[i]);
23321 formatDate : function(date, fmt)
23323 return (!date || !(date instanceof Date)) ?
23324 date : date.dateFormat(fmt || this.format);
23327 onFocus : function()
23329 Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23333 onBlur : function()
23335 Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23337 var d = this.inputEl().getValue();
23344 showPopup : function()
23346 this.picker().show();
23350 this.fireEvent('showpopup', this, this.date);
23353 hidePopup : function()
23355 if(this.isInline) {
23358 this.picker().hide();
23359 this.viewMode = this.startViewMode;
23362 this.fireEvent('hidepopup', this, this.date);
23366 onMousedown: function(e)
23368 e.stopPropagation();
23369 e.preventDefault();
23374 Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23378 setValue: function(v)
23380 if(this.fireEvent('beforeselect', this, v) !== false){
23381 var d = new Date(this.parseDate(v) ).clearTime();
23383 if(isNaN(d.getTime())){
23384 this.date = this.viewDate = '';
23385 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23389 v = this.formatDate(d);
23391 Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23393 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23397 this.fireEvent('select', this, this.date);
23401 getValue: function()
23403 return this.formatDate(this.date);
23406 fireKey: function(e)
23408 if (!this.picker().isVisible()){
23409 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23415 var dateChanged = false,
23417 newDate, newViewDate;
23422 e.preventDefault();
23426 if (!this.keyboardNavigation) {
23429 dir = e.keyCode == 37 ? -1 : 1;
23432 newDate = this.moveYear(this.date, dir);
23433 newViewDate = this.moveYear(this.viewDate, dir);
23434 } else if (e.shiftKey){
23435 newDate = this.moveMonth(this.date, dir);
23436 newViewDate = this.moveMonth(this.viewDate, dir);
23438 newDate = new Date(this.date);
23439 newDate.setUTCDate(this.date.getUTCDate() + dir);
23440 newViewDate = new Date(this.viewDate);
23441 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23443 if (this.dateWithinRange(newDate)){
23444 this.date = newDate;
23445 this.viewDate = newViewDate;
23446 this.setValue(this.formatDate(this.date));
23448 e.preventDefault();
23449 dateChanged = true;
23454 if (!this.keyboardNavigation) {
23457 dir = e.keyCode == 38 ? -1 : 1;
23459 newDate = this.moveYear(this.date, dir);
23460 newViewDate = this.moveYear(this.viewDate, dir);
23461 } else if (e.shiftKey){
23462 newDate = this.moveMonth(this.date, dir);
23463 newViewDate = this.moveMonth(this.viewDate, dir);
23465 newDate = new Date(this.date);
23466 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23467 newViewDate = new Date(this.viewDate);
23468 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23470 if (this.dateWithinRange(newDate)){
23471 this.date = newDate;
23472 this.viewDate = newViewDate;
23473 this.setValue(this.formatDate(this.date));
23475 e.preventDefault();
23476 dateChanged = true;
23480 this.setValue(this.formatDate(this.date));
23482 e.preventDefault();
23485 this.setValue(this.formatDate(this.date));
23499 onClick: function(e)
23501 e.stopPropagation();
23502 e.preventDefault();
23504 var target = e.getTarget();
23506 if(target.nodeName.toLowerCase() === 'i'){
23507 target = Roo.get(target).dom.parentNode;
23510 var nodeName = target.nodeName;
23511 var className = target.className;
23512 var html = target.innerHTML;
23513 //Roo.log(nodeName);
23515 switch(nodeName.toLowerCase()) {
23517 switch(className) {
23523 var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23524 switch(this.viewMode){
23526 this.viewDate = this.moveMonth(this.viewDate, dir);
23530 this.viewDate = this.moveYear(this.viewDate, dir);
23536 var date = new Date();
23537 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23539 this.setValue(this.formatDate(this.date));
23546 if (className.indexOf('disabled') < 0) {
23547 if (!this.viewDate) {
23548 this.viewDate = new Date();
23550 this.viewDate.setUTCDate(1);
23551 if (className.indexOf('month') > -1) {
23552 this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23554 var year = parseInt(html, 10) || 0;
23555 this.viewDate.setUTCFullYear(year);
23559 if(this.singleMode){
23560 this.setValue(this.formatDate(this.viewDate));
23571 //Roo.log(className);
23572 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23573 var day = parseInt(html, 10) || 1;
23574 var year = (this.viewDate || new Date()).getUTCFullYear(),
23575 month = (this.viewDate || new Date()).getUTCMonth();
23577 if (className.indexOf('old') > -1) {
23584 } else if (className.indexOf('new') > -1) {
23592 //Roo.log([year,month,day]);
23593 this.date = this.UTCDate(year, month, day,0,0,0,0);
23594 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23596 //Roo.log(this.formatDate(this.date));
23597 this.setValue(this.formatDate(this.date));
23604 setStartDate: function(startDate)
23606 this.startDate = startDate || -Infinity;
23607 if (this.startDate !== -Infinity) {
23608 this.startDate = this.parseDate(this.startDate);
23611 this.updateNavArrows();
23614 setEndDate: function(endDate)
23616 this.endDate = endDate || Infinity;
23617 if (this.endDate !== Infinity) {
23618 this.endDate = this.parseDate(this.endDate);
23621 this.updateNavArrows();
23624 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23626 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23627 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23628 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23630 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23631 return parseInt(d, 10);
23634 this.updateNavArrows();
23637 updateNavArrows: function()
23639 if(this.singleMode){
23643 var d = new Date(this.viewDate),
23644 year = d.getUTCFullYear(),
23645 month = d.getUTCMonth();
23647 Roo.each(this.picker().select('.prev', true).elements, function(v){
23649 switch (this.viewMode) {
23652 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23658 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23665 Roo.each(this.picker().select('.next', true).elements, function(v){
23667 switch (this.viewMode) {
23670 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23676 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23684 moveMonth: function(date, dir)
23689 var new_date = new Date(date.valueOf()),
23690 day = new_date.getUTCDate(),
23691 month = new_date.getUTCMonth(),
23692 mag = Math.abs(dir),
23694 dir = dir > 0 ? 1 : -1;
23697 // If going back one month, make sure month is not current month
23698 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23700 return new_date.getUTCMonth() == month;
23702 // If going forward one month, make sure month is as expected
23703 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23705 return new_date.getUTCMonth() != new_month;
23707 new_month = month + dir;
23708 new_date.setUTCMonth(new_month);
23709 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23710 if (new_month < 0 || new_month > 11) {
23711 new_month = (new_month + 12) % 12;
23714 // For magnitudes >1, move one month at a time...
23715 for (var i=0; i<mag; i++) {
23716 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23717 new_date = this.moveMonth(new_date, dir);
23719 // ...then reset the day, keeping it in the new month
23720 new_month = new_date.getUTCMonth();
23721 new_date.setUTCDate(day);
23723 return new_month != new_date.getUTCMonth();
23726 // Common date-resetting loop -- if date is beyond end of month, make it
23729 new_date.setUTCDate(--day);
23730 new_date.setUTCMonth(new_month);
23735 moveYear: function(date, dir)
23737 return this.moveMonth(date, dir*12);
23740 dateWithinRange: function(date)
23742 return date >= this.startDate && date <= this.endDate;
23748 this.picker().remove();
23751 validateValue : function(value)
23753 if(this.getVisibilityEl().hasClass('hidden')){
23757 if(value.length < 1) {
23758 if(this.allowBlank){
23764 if(value.length < this.minLength){
23767 if(value.length > this.maxLength){
23771 var vt = Roo.form.VTypes;
23772 if(!vt[this.vtype](value, this)){
23776 if(typeof this.validator == "function"){
23777 var msg = this.validator(value);
23783 if(this.regex && !this.regex.test(value)){
23787 if(typeof(this.parseDate(value)) == 'undefined'){
23791 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23795 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23805 this.date = this.viewDate = '';
23807 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23812 Roo.apply(Roo.bootstrap.form.DateField, {
23823 html: '<i class="fa fa-arrow-left"/>'
23833 html: '<i class="fa fa-arrow-right"/>'
23875 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23876 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23877 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23878 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23879 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23892 navFnc: 'FullYear',
23897 navFnc: 'FullYear',
23902 Roo.apply(Roo.bootstrap.form.DateField, {
23906 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23910 cls: 'datepicker-days',
23914 cls: 'table-condensed',
23916 Roo.bootstrap.form.DateField.head,
23920 Roo.bootstrap.form.DateField.footer
23927 cls: 'datepicker-months',
23931 cls: 'table-condensed',
23933 Roo.bootstrap.form.DateField.head,
23934 Roo.bootstrap.form.DateField.content,
23935 Roo.bootstrap.form.DateField.footer
23942 cls: 'datepicker-years',
23946 cls: 'table-condensed',
23948 Roo.bootstrap.form.DateField.head,
23949 Roo.bootstrap.form.DateField.content,
23950 Roo.bootstrap.form.DateField.footer
23969 * @class Roo.bootstrap.form.TimeField
23970 * @extends Roo.bootstrap.form.Input
23971 * Bootstrap DateField class
23972 * @cfg {Number} minuteStep the minutes is always the multiple of a fixed number, default 1
23976 * Create a new TimeField
23977 * @param {Object} config The config object
23980 Roo.bootstrap.form.TimeField = function(config){
23981 Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23985 * Fires when this field show.
23986 * @param {Roo.bootstrap.form.DateField} thisthis
23987 * @param {Mixed} date The date value
23992 * Fires when this field hide.
23993 * @param {Roo.bootstrap.form.DateField} this
23994 * @param {Mixed} date The date value
23999 * Fires when select a date.
24000 * @param {Roo.bootstrap.form.DateField} this
24001 * @param {Mixed} date The date value
24007 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input, {
24010 * @cfg {String} format
24011 * The default time format string which can be overriden for localization support. The format must be
24012 * valid according to {@link Date#parseDate} (defaults to 'H:i').
24017 getAutoCreate : function()
24019 this.after = '<i class="fa far fa-clock"></i>';
24020 return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
24024 onRender: function(ct, position)
24027 Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
24029 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24031 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24033 this.pop = this.picker().select('>.datepicker-time',true).first();
24034 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24036 this.picker().on('mousedown', this.onMousedown, this);
24037 this.picker().on('click', this.onClick, this);
24039 this.picker().addClass('datepicker-dropdown');
24044 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24045 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24046 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24047 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24048 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24049 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24053 fireKey: function(e){
24054 if (!this.picker().isVisible()){
24055 if (e.keyCode == 27) { // allow escape to hide and re-show picker
24061 e.preventDefault();
24069 this.onTogglePeriod();
24072 this.onIncrementMinutes();
24075 this.onDecrementMinutes();
24084 onClick: function(e) {
24085 e.stopPropagation();
24086 e.preventDefault();
24089 picker : function()
24091 return this.pickerEl;
24094 fillTime: function()
24096 var time = this.pop.select('tbody', true).first();
24098 time.dom.innerHTML = '';
24113 cls: 'hours-up fa fas fa-chevron-up'
24133 cls: 'minutes-up fa fas fa-chevron-up'
24154 cls: 'timepicker-hour',
24169 cls: 'timepicker-minute',
24184 cls: 'btn btn-primary period',
24206 cls: 'hours-down fa fas fa-chevron-down'
24226 cls: 'minutes-down fa fas fa-chevron-down'
24243 // default minute is a multiple of minuteStep
24244 if(typeof(this.time) === 'undefined') {
24245 this.time = new Date();
24246 this.time = this.time.add(Date.MINUTE, Math.round(parseInt(this.time.format('i')) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i')));
24248 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24255 var hours = this.time.getHours();
24256 var minutes = this.time.getMinutes();
24269 hours = hours - 12;
24273 hours = '0' + hours;
24277 minutes = '0' + minutes;
24280 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24281 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24282 this.pop.select('button', true).first().dom.innerHTML = period;
24288 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24290 var cls = ['bottom'];
24292 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24299 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24303 //this.picker().setXY(20000,20000);
24304 this.picker().addClass(cls.join('-'));
24308 Roo.each(cls, function(c){
24313 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
24314 //_this.picker().setTop(_this.inputEl().getHeight());
24318 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
24320 //_this.picker().setTop(0 - _this.picker().getHeight());
24325 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24329 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24337 onFocus : function()
24339 Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24343 onBlur : function()
24345 Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24351 this.picker().show();
24356 this.fireEvent('show', this, this.date);
24361 this.picker().hide();
24364 this.fireEvent('hide', this, this.date);
24367 setTime : function()
24370 this.setValue(this.time.format(this.format));
24372 this.fireEvent('select', this, this.date);
24377 onMousedown: function(e){
24378 e.stopPropagation();
24379 e.preventDefault();
24382 onIncrementHours: function()
24384 Roo.log('onIncrementHours');
24385 this.time = this.time.add(Date.HOUR, 1);
24390 onDecrementHours: function()
24392 Roo.log('onDecrementHours');
24393 this.time = this.time.add(Date.HOUR, -1);
24397 onIncrementMinutes: function()
24399 Roo.log('onIncrementMinutes');
24400 var minutesToAdd = Math.round((parseInt(this.time.format('i')) + this.minuteStep) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i'));
24401 this.time = this.time.add(Date.MINUTE, minutesToAdd);
24405 onDecrementMinutes: function()
24407 Roo.log('onDecrementMinutes');
24408 var minutesToSubtract = parseInt(this.time.format('i')) - Math.round((parseInt(this.time.format('i')) - this.minuteStep) / this.minuteStep) * this.minuteStep;
24409 this.time = this.time.add(Date.MINUTE, -1 * minutesToSubtract);
24413 onTogglePeriod: function()
24415 Roo.log('onTogglePeriod');
24416 this.time = this.time.add(Date.HOUR, 12);
24424 Roo.apply(Roo.bootstrap.form.TimeField, {
24428 cls: 'datepicker dropdown-menu',
24432 cls: 'datepicker-time',
24436 cls: 'table-condensed',
24465 cls: 'btn btn-info ok',
24493 * @class Roo.bootstrap.form.MonthField
24494 * @extends Roo.bootstrap.form.Input
24495 * Bootstrap MonthField class
24497 * @cfg {String} language default en
24500 * Create a new MonthField
24501 * @param {Object} config The config object
24504 Roo.bootstrap.form.MonthField = function(config){
24505 Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24510 * Fires when this field show.
24511 * @param {Roo.bootstrap.form.MonthField} this
24512 * @param {Mixed} date The date value
24517 * Fires when this field hide.
24518 * @param {Roo.bootstrap.form.MonthField} this
24519 * @param {Mixed} date The date value
24524 * Fires when select a date.
24525 * @param {Roo.bootstrap.form.MonthField} this
24526 * @param {String} oldvalue The old value
24527 * @param {String} newvalue The new value
24533 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input, {
24535 onRender: function(ct, position)
24538 Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24540 this.language = this.language || 'en';
24541 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24542 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24544 this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24545 this.isInline = false;
24546 this.isInput = true;
24547 this.component = this.el.select('.add-on', true).first() || false;
24548 this.component = (this.component && this.component.length === 0) ? false : this.component;
24549 this.hasInput = this.component && this.inputEL().length;
24551 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24553 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24555 this.picker().on('mousedown', this.onMousedown, this);
24556 this.picker().on('click', this.onClick, this);
24558 this.picker().addClass('datepicker-dropdown');
24560 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24561 v.setStyle('width', '189px');
24568 if(this.isInline) {
24574 setValue: function(v, suppressEvent)
24576 var o = this.getValue();
24578 Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24582 if(suppressEvent !== true){
24583 this.fireEvent('select', this, o, v);
24588 getValue: function()
24593 onClick: function(e)
24595 e.stopPropagation();
24596 e.preventDefault();
24598 var target = e.getTarget();
24600 if(target.nodeName.toLowerCase() === 'i'){
24601 target = Roo.get(target).dom.parentNode;
24604 var nodeName = target.nodeName;
24605 var className = target.className;
24606 var html = target.innerHTML;
24608 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24612 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24614 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24620 picker : function()
24622 return this.pickerEl;
24625 fillMonths: function()
24628 var months = this.picker().select('>.datepicker-months td', true).first();
24630 months.dom.innerHTML = '';
24636 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24639 months.createChild(month);
24648 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24649 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24652 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24653 e.removeClass('active');
24655 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24656 e.addClass('active');
24663 if(this.isInline) {
24667 this.picker().removeClass(['bottom', 'top']);
24669 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24671 * place to the top of element!
24675 this.picker().addClass('top');
24676 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24681 this.picker().addClass('bottom');
24683 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24686 onFocus : function()
24688 Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24692 onBlur : function()
24694 Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24696 var d = this.inputEl().getValue();
24705 this.picker().show();
24706 this.picker().select('>.datepicker-months', true).first().show();
24710 this.fireEvent('show', this, this.date);
24715 if(this.isInline) {
24718 this.picker().hide();
24719 this.fireEvent('hide', this, this.date);
24723 onMousedown: function(e)
24725 e.stopPropagation();
24726 e.preventDefault();
24731 Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24735 fireKey: function(e)
24737 if (!this.picker().isVisible()){
24738 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24749 e.preventDefault();
24753 dir = e.keyCode == 37 ? -1 : 1;
24755 this.vIndex = this.vIndex + dir;
24757 if(this.vIndex < 0){
24761 if(this.vIndex > 11){
24765 if(isNaN(this.vIndex)){
24769 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24775 dir = e.keyCode == 38 ? -1 : 1;
24777 this.vIndex = this.vIndex + dir * 4;
24779 if(this.vIndex < 0){
24783 if(this.vIndex > 11){
24787 if(isNaN(this.vIndex)){
24791 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24796 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24797 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24801 e.preventDefault();
24804 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24805 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24821 this.picker().remove();
24826 Roo.apply(Roo.bootstrap.form.MonthField, {
24845 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24846 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24851 Roo.apply(Roo.bootstrap.form.MonthField, {
24855 cls: 'datepicker dropdown-menu roo-dynamic',
24859 cls: 'datepicker-months',
24863 cls: 'table-condensed',
24865 Roo.bootstrap.form.DateField.content
24885 * @class Roo.bootstrap.form.CheckBox
24886 * @extends Roo.bootstrap.form.Input
24887 * Bootstrap CheckBox class
24889 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24890 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24891 * @cfg {String} boxLabel The text that appears beside the checkbox
24892 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24893 * @cfg {Boolean} checked initnal the element
24894 * @cfg {Boolean} inline inline the element (default false)
24895 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24896 * @cfg {String} tooltip label tooltip
24899 * Create a new CheckBox
24900 * @param {Object} config The config object
24903 Roo.bootstrap.form.CheckBox = function(config){
24904 Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24909 * Fires when the element is checked or unchecked.
24910 * @param {Roo.bootstrap.form.CheckBox} this This input
24911 * @param {Boolean} checked The new checked value
24916 * Fires when the element is click.
24917 * @param {Roo.bootstrap.form.CheckBox} this This input
24924 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input, {
24926 inputType: 'checkbox',
24935 // checkbox success does not make any sense really..
24940 getAutoCreate : function()
24942 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24948 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24951 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24957 type : this.inputType,
24958 value : this.inputValue,
24959 cls : 'roo-' + this.inputType, //'form-box',
24960 placeholder : this.placeholder || ''
24964 if(this.inputType != 'radio'){
24968 cls : 'roo-hidden-value',
24969 value : this.checked ? this.inputValue : this.valueOff
24974 if (this.weight) { // Validity check?
24975 cfg.cls += " " + this.inputType + "-" + this.weight;
24978 if (this.disabled) {
24979 input.disabled=true;
24983 input.checked = this.checked;
24988 input.name = this.name;
24990 if(this.inputType != 'radio'){
24991 hidden.name = this.name;
24992 input.name = '_hidden_' + this.name;
24997 input.cls += ' input-' + this.size;
25002 ['xs','sm','md','lg'].map(function(size){
25003 if (settings[size]) {
25004 cfg.cls += ' col-' + size + '-' + settings[size];
25008 var inputblock = input;
25010 if (this.before || this.after) {
25013 cls : 'input-group',
25018 inputblock.cn.push({
25020 cls : 'input-group-addon',
25025 inputblock.cn.push(input);
25027 if(this.inputType != 'radio'){
25028 inputblock.cn.push(hidden);
25032 inputblock.cn.push({
25034 cls : 'input-group-addon',
25040 var boxLabelCfg = false;
25046 //'for': id, // box label is handled by onclick - so no for...
25048 html: this.boxLabel
25051 boxLabelCfg.tooltip = this.tooltip;
25057 if (align ==='left' && this.fieldLabel.length) {
25058 // Roo.log("left and has label");
25063 cls : 'control-label',
25064 html : this.fieldLabel
25075 cfg.cn[1].cn.push(boxLabelCfg);
25078 if(this.labelWidth > 12){
25079 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25082 if(this.labelWidth < 13 && this.labelmd == 0){
25083 this.labelmd = this.labelWidth;
25086 if(this.labellg > 0){
25087 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25088 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25091 if(this.labelmd > 0){
25092 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25093 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25096 if(this.labelsm > 0){
25097 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25098 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25101 if(this.labelxs > 0){
25102 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25103 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25106 } else if ( this.fieldLabel.length) {
25107 // Roo.log(" label");
25111 tag: this.boxLabel ? 'span' : 'label',
25113 cls: 'control-label box-input-label',
25114 //cls : 'input-group-addon',
25115 html : this.fieldLabel
25122 cfg.cn.push(boxLabelCfg);
25127 // Roo.log(" no label && no align");
25128 cfg.cn = [ inputblock ] ;
25130 cfg.cn.push(boxLabelCfg);
25138 if(this.inputType != 'radio'){
25139 cfg.cn.push(hidden);
25147 * return the real input element.
25149 inputEl: function ()
25151 return this.el.select('input.roo-' + this.inputType,true).first();
25153 hiddenEl: function ()
25155 return this.el.select('input.roo-hidden-value',true).first();
25158 labelEl: function()
25160 return this.el.select('label.control-label',true).first();
25162 /* depricated... */
25166 return this.labelEl();
25169 boxLabelEl: function()
25171 return this.el.select('label.box-label',true).first();
25174 initEvents : function()
25176 // Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25178 this.inputEl().on('click', this.onClick, this);
25180 if (this.boxLabel) {
25181 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
25184 this.startValue = this.getValue();
25187 Roo.bootstrap.form.CheckBox.register(this);
25191 onClick : function(e)
25193 if(this.fireEvent('click', this, e) !== false){
25194 this.setChecked(!this.checked);
25199 setChecked : function(state,suppressEvent)
25201 this.startValue = this.getValue();
25203 if(this.inputType == 'radio'){
25205 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25206 e.dom.checked = false;
25209 this.inputEl().dom.checked = true;
25211 this.inputEl().dom.value = this.inputValue;
25213 if(suppressEvent !== true){
25214 this.fireEvent('check', this, true);
25222 this.checked = state;
25224 this.inputEl().dom.checked = state;
25227 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25229 if(suppressEvent !== true){
25230 this.fireEvent('check', this, state);
25236 getValue : function()
25238 if(this.inputType == 'radio'){
25239 return this.getGroupValue();
25242 return this.hiddenEl().dom.value;
25246 getGroupValue : function()
25248 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25252 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25255 setValue : function(v,suppressEvent)
25257 if(this.inputType == 'radio'){
25258 this.setGroupValue(v, suppressEvent);
25262 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25267 setGroupValue : function(v, suppressEvent)
25269 this.startValue = this.getValue();
25271 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25272 e.dom.checked = false;
25274 if(e.dom.value == v){
25275 e.dom.checked = true;
25279 if(suppressEvent !== true){
25280 this.fireEvent('check', this, true);
25288 validate : function()
25290 if(this.getVisibilityEl().hasClass('hidden')){
25296 (this.inputType == 'radio' && this.validateRadio()) ||
25297 (this.inputType == 'checkbox' && this.validateCheckbox())
25303 this.markInvalid();
25307 validateRadio : function()
25309 if(this.getVisibilityEl().hasClass('hidden')){
25313 if(this.allowBlank){
25319 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25320 if(!e.dom.checked){
25332 validateCheckbox : function()
25335 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25336 //return (this.getValue() == this.inputValue) ? true : false;
25339 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25347 for(var i in group){
25348 if(group[i].el.isVisible(true)){
25356 for(var i in group){
25361 r = (group[i].getValue() == group[i].inputValue) ? true : false;
25368 * Mark this field as valid
25370 markValid : function()
25374 this.fireEvent('valid', this);
25376 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25379 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25386 if(this.inputType == 'radio'){
25387 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25388 var fg = e.findParent('.form-group', false, true);
25389 if (Roo.bootstrap.version == 3) {
25390 fg.removeClass([_this.invalidClass, _this.validClass]);
25391 fg.addClass(_this.validClass);
25393 fg.removeClass(['is-valid', 'is-invalid']);
25394 fg.addClass('is-valid');
25402 var fg = this.el.findParent('.form-group', false, true);
25403 if (Roo.bootstrap.version == 3) {
25404 fg.removeClass([this.invalidClass, this.validClass]);
25405 fg.addClass(this.validClass);
25407 fg.removeClass(['is-valid', 'is-invalid']);
25408 fg.addClass('is-valid');
25413 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25419 for(var i in group){
25420 var fg = group[i].el.findParent('.form-group', false, true);
25421 if (Roo.bootstrap.version == 3) {
25422 fg.removeClass([this.invalidClass, this.validClass]);
25423 fg.addClass(this.validClass);
25425 fg.removeClass(['is-valid', 'is-invalid']);
25426 fg.addClass('is-valid');
25432 * Mark this field as invalid
25433 * @param {String} msg The validation message
25435 markInvalid : function(msg)
25437 if(this.allowBlank){
25443 this.fireEvent('invalid', this, msg);
25445 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25448 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25452 label.markInvalid();
25455 if(this.inputType == 'radio'){
25457 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25458 var fg = e.findParent('.form-group', false, true);
25459 if (Roo.bootstrap.version == 3) {
25460 fg.removeClass([_this.invalidClass, _this.validClass]);
25461 fg.addClass(_this.invalidClass);
25463 fg.removeClass(['is-invalid', 'is-valid']);
25464 fg.addClass('is-invalid');
25472 var fg = this.el.findParent('.form-group', false, true);
25473 if (Roo.bootstrap.version == 3) {
25474 fg.removeClass([_this.invalidClass, _this.validClass]);
25475 fg.addClass(_this.invalidClass);
25477 fg.removeClass(['is-invalid', 'is-valid']);
25478 fg.addClass('is-invalid');
25483 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25489 for(var i in group){
25490 var fg = group[i].el.findParent('.form-group', false, true);
25491 if (Roo.bootstrap.version == 3) {
25492 fg.removeClass([_this.invalidClass, _this.validClass]);
25493 fg.addClass(_this.invalidClass);
25495 fg.removeClass(['is-invalid', 'is-valid']);
25496 fg.addClass('is-invalid');
25502 clearInvalid : function()
25504 Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25506 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25508 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25510 if (label && label.iconEl) {
25511 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25512 label.iconEl.removeClass(['is-invalid', 'is-valid']);
25516 disable : function()
25518 if(this.inputType != 'radio'){
25519 Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25526 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25527 _this.getActionEl().addClass(this.disabledClass);
25528 e.dom.disabled = true;
25532 this.disabled = true;
25533 this.fireEvent("disable", this);
25537 enable : function()
25539 if(this.inputType != 'radio'){
25540 Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25547 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25548 _this.getActionEl().removeClass(this.disabledClass);
25549 e.dom.disabled = false;
25553 this.disabled = false;
25554 this.fireEvent("enable", this);
25558 setBoxLabel : function(v)
25563 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25569 Roo.apply(Roo.bootstrap.form.CheckBox, {
25574 * register a CheckBox Group
25575 * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25577 register : function(checkbox)
25579 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25580 this.groups[checkbox.groupId] = {};
25583 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25587 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25591 * fetch a CheckBox Group based on the group ID
25592 * @param {string} the group ID
25593 * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25595 get: function(groupId) {
25596 if (typeof(this.groups[groupId]) == 'undefined') {
25600 return this.groups[groupId] ;
25613 * @class Roo.bootstrap.form.Radio
25614 * @extends Roo.bootstrap.Component
25615 * Bootstrap Radio class
25616 * @cfg {String} boxLabel - the label associated
25617 * @cfg {String} value - the value of radio
25620 * Create a new Radio
25621 * @param {Object} config The config object
25623 Roo.bootstrap.form.Radio = function(config){
25624 Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25628 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25634 getAutoCreate : function()
25638 cls : 'form-group radio',
25643 html : this.boxLabel
25651 initEvents : function()
25653 this.parent().register(this);
25655 this.el.on('click', this.onClick, this);
25659 onClick : function(e)
25661 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25662 this.setChecked(true);
25666 setChecked : function(state, suppressEvent)
25668 this.parent().setValue(this.value, suppressEvent);
25672 setBoxLabel : function(v)
25677 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25692 * @class Roo.bootstrap.form.SecurePass
25693 * @extends Roo.bootstrap.form.Input
25694 * Bootstrap SecurePass class
25698 * Create a new SecurePass
25699 * @param {Object} config The config object
25702 Roo.bootstrap.form.SecurePass = function (config) {
25703 // these go here, so the translation tool can replace them..
25705 PwdEmpty: "Please type a password, and then retype it to confirm.",
25706 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25707 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25708 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25709 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25710 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25711 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25712 TooWeak: "Your password is Too Weak."
25714 this.meterLabel = "Password strength:";
25715 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25716 this.meterClass = [
25717 "roo-password-meter-tooweak",
25718 "roo-password-meter-weak",
25719 "roo-password-meter-medium",
25720 "roo-password-meter-strong",
25721 "roo-password-meter-grey"
25726 Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25729 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25731 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25733 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25734 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25735 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25736 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25737 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25738 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25739 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25749 * @cfg {String/Object} Label for the strength meter (defaults to
25750 * 'Password strength:')
25755 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25756 * ['Weak', 'Medium', 'Strong'])
25759 pwdStrengths: false,
25772 initEvents: function ()
25774 Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25776 if (this.el.is('input[type=password]') && Roo.isSafari) {
25777 this.el.on('keydown', this.SafariOnKeyDown, this);
25780 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25783 onRender: function (ct, position)
25785 Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25786 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25787 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25789 this.trigger.createChild({
25794 cls: 'roo-password-meter-grey col-xs-12',
25797 //width: this.meterWidth + 'px'
25801 cls: 'roo-password-meter-text'
25807 if (this.hideTrigger) {
25808 this.trigger.setDisplayed(false);
25810 this.setSize(this.width || '', this.height || '');
25813 onDestroy: function ()
25815 if (this.trigger) {
25816 this.trigger.removeAllListeners();
25817 this.trigger.remove();
25820 this.wrap.remove();
25822 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25825 checkStrength: function ()
25827 var pwd = this.inputEl().getValue();
25828 if (pwd == this._lastPwd) {
25833 if (this.ClientSideStrongPassword(pwd)) {
25835 } else if (this.ClientSideMediumPassword(pwd)) {
25837 } else if (this.ClientSideWeakPassword(pwd)) {
25843 Roo.log('strength1: ' + strength);
25845 //var pm = this.trigger.child('div/div/div').dom;
25846 var pm = this.trigger.child('div/div');
25847 pm.removeClass(this.meterClass);
25848 pm.addClass(this.meterClass[strength]);
25851 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25853 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25855 this._lastPwd = pwd;
25859 Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25861 this._lastPwd = '';
25863 var pm = this.trigger.child('div/div');
25864 pm.removeClass(this.meterClass);
25865 pm.addClass('roo-password-meter-grey');
25868 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25871 this.inputEl().dom.type='password';
25874 validateValue: function (value)
25876 if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25879 if (value.length == 0) {
25880 if (this.allowBlank) {
25881 this.clearInvalid();
25885 this.markInvalid(this.errors.PwdEmpty);
25886 this.errorMsg = this.errors.PwdEmpty;
25894 if (!value.match(/[\x21-\x7e]+/)) {
25895 this.markInvalid(this.errors.PwdBadChar);
25896 this.errorMsg = this.errors.PwdBadChar;
25899 if (value.length < 6) {
25900 this.markInvalid(this.errors.PwdShort);
25901 this.errorMsg = this.errors.PwdShort;
25904 if (value.length > 16) {
25905 this.markInvalid(this.errors.PwdLong);
25906 this.errorMsg = this.errors.PwdLong;
25910 if (this.ClientSideStrongPassword(value)) {
25912 } else if (this.ClientSideMediumPassword(value)) {
25914 } else if (this.ClientSideWeakPassword(value)) {
25921 if (strength < 2) {
25922 //this.markInvalid(this.errors.TooWeak);
25923 this.errorMsg = this.errors.TooWeak;
25928 console.log('strength2: ' + strength);
25930 //var pm = this.trigger.child('div/div/div').dom;
25932 var pm = this.trigger.child('div/div');
25933 pm.removeClass(this.meterClass);
25934 pm.addClass(this.meterClass[strength]);
25936 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25938 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25940 this.errorMsg = '';
25944 CharacterSetChecks: function (type)
25947 this.fResult = false;
25950 isctype: function (character, type)
25953 case this.kCapitalLetter:
25954 if (character >= 'A' && character <= 'Z') {
25959 case this.kSmallLetter:
25960 if (character >= 'a' && character <= 'z') {
25966 if (character >= '0' && character <= '9') {
25971 case this.kPunctuation:
25972 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25983 IsLongEnough: function (pwd, size)
25985 return !(pwd == null || isNaN(size) || pwd.length < size);
25988 SpansEnoughCharacterSets: function (word, nb)
25990 if (!this.IsLongEnough(word, nb))
25995 var characterSetChecks = new Array(
25996 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25997 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
26000 for (var index = 0; index < word.length; ++index) {
26001 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
26002 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
26003 characterSetChecks[nCharSet].fResult = true;
26010 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
26011 if (characterSetChecks[nCharSet].fResult) {
26016 if (nCharSets < nb) {
26022 ClientSideStrongPassword: function (pwd)
26024 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
26027 ClientSideMediumPassword: function (pwd)
26029 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
26032 ClientSideWeakPassword: function (pwd)
26034 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26038 * @class Roo.bootstrap.form.Password
26039 * @extends Roo.bootstrap.form.Input
26040 * Bootstrap Password class
26046 * Create a new Password
26047 * @param {Object} config The config object
26050 Roo.bootstrap.form.Password = function(config){
26051 Roo.bootstrap.form.Password.superclass.constructor.call(this, config);
26053 this.inputType = 'password';
26056 Roo.extend(Roo.bootstrap.form.Password, Roo.bootstrap.form.Input, {
26058 initEvents : function()
26060 Roo.bootstrap.form.Password.superclass.initEvents.call(this);
26062 this.el.addClass('form-password');
26064 this.inputEl().addClass('password-hidden');
26066 this.inputEl().on('click', this.onPasswordClick, this);
26069 onPasswordClick : function(e)
26071 var input = this.inputEl();
26073 if(e.getPageX() < input.getX() + input.getWidth() - 30) {
26077 input.removeClass(['password-visible', 'password-hidden']);
26079 if(input.attr('type') == 'password') {
26080 input.attr('type', 'text');
26081 input.addClass('password-visible');
26084 input.attr('type', 'password');
26085 input.addClass('password-hidden');
26088 });Roo.rtf = {}; // namespace
26089 Roo.rtf.Hex = function(hex)
26093 Roo.rtf.Paragraph = function(opts)
26095 this.content = []; ///??? is that used?
26096 };Roo.rtf.Span = function(opts)
26098 this.value = opts.value;
26101 Roo.rtf.Group = function(parent)
26103 // we dont want to acutally store parent - it will make debug a nightmare..
26111 Roo.rtf.Group.prototype = {
26115 addContent : function(node) {
26116 // could set styles...
26117 this.content.push(node);
26119 addChild : function(cn)
26123 // only for images really...
26124 toDataURL : function()
26126 var mimetype = false;
26128 case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0:
26129 mimetype = "image/png";
26131 case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
26132 mimetype = "image/jpeg";
26135 return 'about:blank'; // ?? error?
26139 var hexstring = this.content[this.content.length-1].value;
26141 return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
26142 return String.fromCharCode(parseInt(a, 16));
26147 // this looks like it's normally the {rtf{ .... }}
26148 Roo.rtf.Document = function()
26150 // we dont want to acutally store parent - it will make debug a nightmare..
26156 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, {
26157 addChild : function(cn)
26161 case 'rtlch': // most content seems to be inside this??
26164 this.rtlch.push(cn);
26167 this[cn.type] = cn;
26172 getElementsByType : function(type)
26175 this._getElementsByType(type, ret, this.cn, 'rtf');
26178 _getElementsByType : function (type, ret, search_array, path)
26180 search_array.forEach(function(n,i) {
26181 if (n.type == type) {
26182 n.path = path + '/' + n.type + ':' + i;
26185 if (n.cn.length > 0) {
26186 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
26193 Roo.rtf.Ctrl = function(opts)
26195 this.value = opts.value;
26196 this.param = opts.param;
26201 * based on this https://github.com/iarna/rtf-parser
26202 * it's really only designed to extract pict from pasted RTF
26206 * var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
26215 Roo.rtf.Parser = function(text) {
26216 //super({objectMode: true})
26218 this.parserState = this.parseText;
26220 // these are for interpeter...
26222 ///this.parserState = this.parseTop
26223 this.groupStack = [];
26224 this.hexStore = [];
26227 this.groups = []; // where we put the return.
26229 for (var ii = 0; ii < text.length; ++ii) {
26232 if (text[ii] === '\n') {
26238 this.parserState(text[ii]);
26244 Roo.rtf.Parser.prototype = {
26245 text : '', // string being parsed..
26247 controlWordParam : '',
26251 groupStack : false,
26256 row : 1, // reportin?
26260 push : function (el)
26262 var m = 'cmd'+ el.type;
26263 if (typeof(this[m]) == 'undefined') {
26264 Roo.log('invalid cmd:' + el.type);
26270 flushHexStore : function()
26272 if (this.hexStore.length < 1) {
26275 var hexstr = this.hexStore.map(
26280 this.group.addContent( new Roo.rtf.Hex( hexstr ));
26283 this.hexStore.splice(0)
26287 cmdgroupstart : function()
26289 this.flushHexStore();
26291 this.groupStack.push(this.group);
26294 if (this.doc === false) {
26295 this.group = this.doc = new Roo.rtf.Document();
26299 this.group = new Roo.rtf.Group(this.group);
26301 cmdignorable : function()
26303 this.flushHexStore();
26304 this.group.ignorable = true;
26306 cmdendparagraph : function()
26308 this.flushHexStore();
26309 this.group.addContent(new Roo.rtf.Paragraph());
26311 cmdgroupend : function ()
26313 this.flushHexStore();
26314 var endingGroup = this.group;
26317 this.group = this.groupStack.pop();
26319 this.group.addChild(endingGroup);
26324 var doc = this.group || this.doc;
26325 //if (endingGroup instanceof FontTable) {
26326 // doc.fonts = endingGroup.table
26327 //} else if (endingGroup instanceof ColorTable) {
26328 // doc.colors = endingGroup.table
26329 //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
26330 if (endingGroup.ignorable === false) {
26332 this.groups.push(endingGroup);
26333 // Roo.log( endingGroup );
26335 //Roo.each(endingGroup.content, function(item)) {
26336 // doc.addContent(item);
26338 //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
26341 cmdtext : function (cmd)
26343 this.flushHexStore();
26344 if (!this.group) { // an RTF fragment, missing the {\rtf1 header
26345 //this.group = this.doc
26346 return; // we really don't care about stray text...
26348 this.group.addContent(new Roo.rtf.Span(cmd));
26350 cmdcontrolword : function (cmd)
26352 this.flushHexStore();
26353 if (!this.group.type) {
26354 this.group.type = cmd.value;
26357 this.group.addContent(new Roo.rtf.Ctrl(cmd));
26358 // we actually don't care about ctrl words...
26361 var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
26362 if (this[method]) {
26363 this[method](cmd.param)
26365 if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
26369 cmdhexchar : function(cmd) {
26370 this.hexStore.push(cmd);
26372 cmderror : function(cmd) {
26378 if (this.text !== '\u0000') this.emitText()
26384 parseText : function(c)
26387 this.parserState = this.parseEscapes;
26388 } else if (c === '{') {
26389 this.emitStartGroup();
26390 } else if (c === '}') {
26391 this.emitEndGroup();
26392 } else if (c === '\x0A' || c === '\x0D') {
26393 // cr/lf are noise chars
26399 parseEscapes: function (c)
26401 if (c === '\\' || c === '{' || c === '}') {
26403 this.parserState = this.parseText;
26405 this.parserState = this.parseControlSymbol;
26406 this.parseControlSymbol(c);
26409 parseControlSymbol: function(c)
26412 this.text += '\u00a0'; // nbsp
26413 this.parserState = this.parseText
26414 } else if (c === '-') {
26415 this.text += '\u00ad'; // soft hyphen
26416 } else if (c === '_') {
26417 this.text += '\u2011'; // non-breaking hyphen
26418 } else if (c === '*') {
26419 this.emitIgnorable();
26420 this.parserState = this.parseText;
26421 } else if (c === "'") {
26422 this.parserState = this.parseHexChar;
26423 } else if (c === '|') { // formula cacter
26424 this.emitFormula();
26425 this.parserState = this.parseText;
26426 } else if (c === ':') { // subentry in an index entry
26427 this.emitIndexSubEntry();
26428 this.parserState = this.parseText;
26429 } else if (c === '\x0a') {
26430 this.emitEndParagraph();
26431 this.parserState = this.parseText;
26432 } else if (c === '\x0d') {
26433 this.emitEndParagraph();
26434 this.parserState = this.parseText;
26436 this.parserState = this.parseControlWord;
26437 this.parseControlWord(c);
26440 parseHexChar: function (c)
26442 if (/^[A-Fa-f0-9]$/.test(c)) {
26444 if (this.hexChar.length >= 2) {
26445 this.emitHexChar();
26446 this.parserState = this.parseText;
26450 this.emitError("Invalid character \"" + c + "\" in hex literal.");
26451 this.parserState = this.parseText;
26454 parseControlWord : function(c)
26457 this.emitControlWord();
26458 this.parserState = this.parseText;
26459 } else if (/^[-\d]$/.test(c)) {
26460 this.parserState = this.parseControlWordParam;
26461 this.controlWordParam += c;
26462 } else if (/^[A-Za-z]$/.test(c)) {
26463 this.controlWord += c;
26465 this.emitControlWord();
26466 this.parserState = this.parseText;
26470 parseControlWordParam : function (c) {
26471 if (/^\d$/.test(c)) {
26472 this.controlWordParam += c;
26473 } else if (c === ' ') {
26474 this.emitControlWord();
26475 this.parserState = this.parseText;
26477 this.emitControlWord();
26478 this.parserState = this.parseText;
26486 emitText : function () {
26487 if (this.text === '') {
26499 emitControlWord : function ()
26502 if (this.controlWord === '') {
26503 // do we want to track this - it seems just to cause problems.
26504 //this.emitError('empty control word');
26507 type: 'controlword',
26508 value: this.controlWord,
26509 param: this.controlWordParam !== '' && Number(this.controlWordParam),
26515 this.controlWord = '';
26516 this.controlWordParam = '';
26518 emitStartGroup : function ()
26522 type: 'groupstart',
26528 emitEndGroup : function ()
26538 emitIgnorable : function ()
26548 emitHexChar : function ()
26553 value: this.hexChar,
26560 emitError : function (message)
26568 char: this.cpos //,
26569 //stack: new Error().stack
26572 emitEndParagraph : function () {
26575 type: 'endparagraph',
26584 * @class Roo.htmleditor.Filter
26585 * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26586 * @cfg {DomElement} node The node to iterate and filter
26587 * @cfg {boolean|String|Array} tag Tags to replace
26589 * Create a new Filter.
26590 * @param {Object} config Configuration options
26595 Roo.htmleditor.Filter = function(cfg) {
26596 Roo.apply(this.cfg);
26597 // this does not actually call walk as it's really just a abstract class
26601 Roo.htmleditor.Filter.prototype = {
26607 // overrride to do replace comments.
26608 replaceComment : false,
26610 // overrride to do replace or do stuff with tags..
26611 replaceTag : false,
26613 walk : function(dom)
26615 Roo.each( Array.from(dom.childNodes), function( e ) {
26618 case e.nodeType == 8 && this.replaceComment !== false: // comment
26619 this.replaceComment(e);
26622 case e.nodeType != 1: //not a node.
26625 case this.tag === true: // everything
26626 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26627 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26628 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26629 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26630 if (this.replaceTag && false === this.replaceTag(e)) {
26633 if (e.hasChildNodes()) {
26638 default: // tags .. that do not match.
26639 if (e.hasChildNodes()) {
26649 removeNodeKeepChildren : function( node)
26652 ar = Array.from(node.childNodes);
26653 for (var i = 0; i < ar.length; i++) {
26655 node.removeChild(ar[i]);
26656 // what if we need to walk these???
26657 node.parentNode.insertBefore(ar[i], node);
26660 node.parentNode.removeChild(node);
26665 * @class Roo.htmleditor.FilterAttributes
26666 * clean attributes and styles including http:// etc.. in attribute
26668 * Run a new Attribute Filter
26669 * @param {Object} config Configuration options
26671 Roo.htmleditor.FilterAttributes = function(cfg)
26673 Roo.apply(this, cfg);
26674 this.attrib_black = this.attrib_black || [];
26675 this.attrib_white = this.attrib_white || [];
26677 this.attrib_clean = this.attrib_clean || [];
26678 this.style_white = this.style_white || [];
26679 this.style_black = this.style_black || [];
26680 this.walk(cfg.node);
26683 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26685 tag: true, // all tags
26687 attrib_black : false, // array
26688 attrib_clean : false,
26689 attrib_white : false,
26691 style_white : false,
26692 style_black : false,
26695 replaceTag : function(node)
26697 if (!node.attributes || !node.attributes.length) {
26701 for (var i = node.attributes.length-1; i > -1 ; i--) {
26702 var a = node.attributes[i];
26704 if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26705 node.removeAttribute(a.name);
26711 if (a.name.toLowerCase().substr(0,2)=='on') {
26712 node.removeAttribute(a.name);
26717 if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26718 node.removeAttribute(a.name);
26721 if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26722 this.cleanAttr(node,a.name,a.value); // fixme..
26725 if (a.name == 'style') {
26726 this.cleanStyle(node,a.name,a.value);
26729 /// clean up MS crap..
26730 // tecnically this should be a list of valid class'es..
26733 if (a.name == 'class') {
26734 if (a.value.match(/^Mso/)) {
26735 node.removeAttribute('class');
26738 if (a.value.match(/^body$/)) {
26739 node.removeAttribute('class');
26749 return true; // clean children
26752 cleanAttr: function(node, n,v)
26755 if (v.match(/^\./) || v.match(/^\//)) {
26758 if (v.match(/^(http|https):\/\//)
26759 || v.match(/^mailto:/)
26760 || v.match(/^ftp:/)
26761 || v.match(/^data:/)
26765 if (v.match(/^#/)) {
26768 if (v.match(/^\{/)) { // allow template editing.
26771 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26772 node.removeAttribute(n);
26775 cleanStyle : function(node, n,v)
26777 if (v.match(/expression/)) { //XSS?? should we even bother..
26778 node.removeAttribute(n);
26782 var parts = v.split(/;/);
26785 Roo.each(parts, function(p) {
26786 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26790 var l = p.split(':').shift().replace(/\s+/g,'');
26791 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26793 if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26797 // only allow 'c whitelisted system attributes'
26798 if ( this.style_white.length && style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26806 if (clean.length) {
26807 node.setAttribute(n, clean.join(';'));
26809 node.removeAttribute(n);
26818 * @class Roo.htmleditor.FilterBlack
26819 * remove blacklisted elements.
26821 * Run a new Blacklisted Filter
26822 * @param {Object} config Configuration options
26825 Roo.htmleditor.FilterBlack = function(cfg)
26827 Roo.apply(this, cfg);
26828 this.walk(cfg.node);
26831 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26833 tag : true, // all elements.
26835 replaceTag : function(n)
26837 n.parentNode.removeChild(n);
26841 * @class Roo.htmleditor.FilterComment
26844 * Run a new Comments Filter
26845 * @param {Object} config Configuration options
26847 Roo.htmleditor.FilterComment = function(cfg)
26849 this.walk(cfg.node);
26852 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26855 replaceComment : function(n)
26857 n.parentNode.removeChild(n);
26860 * @class Roo.htmleditor.FilterKeepChildren
26861 * remove tags but keep children
26863 * Run a new Keep Children Filter
26864 * @param {Object} config Configuration options
26867 Roo.htmleditor.FilterKeepChildren = function(cfg)
26869 Roo.apply(this, cfg);
26870 if (this.tag === false) {
26871 return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26874 if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26875 this.cleanNamespace = true;
26878 this.walk(cfg.node);
26881 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26883 cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26885 replaceTag : function(node)
26887 // walk children...
26888 //Roo.log(node.tagName);
26889 var ar = Array.from(node.childNodes);
26892 for (var i = 0; i < ar.length; i++) {
26894 if (e.nodeType == 1) {
26896 (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26897 || // array and it matches
26898 (typeof(this.tag) == 'string' && this.tag == e.tagName)
26900 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26902 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26904 this.replaceTag(ar[i]); // child is blacklisted as well...
26909 ar = Array.from(node.childNodes);
26910 for (var i = 0; i < ar.length; i++) {
26912 node.removeChild(ar[i]);
26913 // what if we need to walk these???
26914 node.parentNode.insertBefore(ar[i], node);
26915 if (this.tag !== false) {
26920 //Roo.log("REMOVE:" + node.tagName);
26921 node.parentNode.removeChild(node);
26922 return false; // don't walk children
26927 * @class Roo.htmleditor.FilterParagraph
26928 * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26929 * like on 'push' to remove the <p> tags and replace them with line breaks.
26931 * Run a new Paragraph Filter
26932 * @param {Object} config Configuration options
26935 Roo.htmleditor.FilterParagraph = function(cfg)
26937 // no need to apply config.
26938 this.walk(cfg.node);
26941 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26948 replaceTag : function(node)
26951 if (node.childNodes.length == 1 &&
26952 node.childNodes[0].nodeType == 3 &&
26953 node.childNodes[0].textContent.trim().length < 1
26955 // remove and replace with '<BR>';
26956 node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26957 return false; // no need to walk..
26959 var ar = Array.from(node.childNodes);
26960 for (var i = 0; i < ar.length; i++) {
26961 node.removeChild(ar[i]);
26962 // what if we need to walk these???
26963 node.parentNode.insertBefore(ar[i], node);
26965 // now what about this?
26969 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26970 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26971 node.parentNode.removeChild(node);
26978 * @class Roo.htmleditor.FilterSpan
26979 * filter span's with no attributes out..
26981 * Run a new Span Filter
26982 * @param {Object} config Configuration options
26985 Roo.htmleditor.FilterSpan = function(cfg)
26987 // no need to apply config.
26988 this.walk(cfg.node);
26991 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26997 replaceTag : function(node)
26999 if (node.attributes && node.attributes.length > 0) {
27000 return true; // walk if there are any.
27002 Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
27008 * @class Roo.htmleditor.FilterTableWidth
27009 try and remove table width data - as that frequently messes up other stuff.
27011 * was cleanTableWidths.
27013 * Quite often pasting from word etc.. results in tables with column and widths.
27014 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
27017 * Run a new Table Filter
27018 * @param {Object} config Configuration options
27021 Roo.htmleditor.FilterTableWidth = function(cfg)
27023 // no need to apply config.
27024 this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
27025 this.walk(cfg.node);
27028 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
27033 replaceTag: function(node) {
27037 if (node.hasAttribute('width')) {
27038 node.removeAttribute('width');
27042 if (node.hasAttribute("style")) {
27045 var styles = node.getAttribute("style").split(";");
27047 Roo.each(styles, function(s) {
27048 if (!s.match(/:/)) {
27051 var kv = s.split(":");
27052 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27055 // what ever is left... we allow.
27058 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27059 if (!nstyle.length) {
27060 node.removeAttribute('style');
27064 return true; // continue doing children..
27067 * @class Roo.htmleditor.FilterWord
27068 * try and clean up all the mess that Word generates.
27070 * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters
27073 * Run a new Span Filter
27074 * @param {Object} config Configuration options
27077 Roo.htmleditor.FilterWord = function(cfg)
27079 // no need to apply config.
27080 this.replaceDocBullets(cfg.node);
27082 this.replaceAname(cfg.node);
27083 // this is disabled as the removal is done by other filters;
27084 // this.walk(cfg.node);
27085 this.replaceImageTable(cfg.node);
27089 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
27095 * Clean up MS wordisms...
27097 replaceTag : function(node)
27100 // no idea what this does - span with text, replaceds with just text.
27102 node.nodeName == 'SPAN' &&
27103 !node.hasAttributes() &&
27104 node.childNodes.length == 1 &&
27105 node.firstChild.nodeName == "#text"
27107 var textNode = node.firstChild;
27108 node.removeChild(textNode);
27109 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27110 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27112 node.parentNode.insertBefore(textNode, node);
27113 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27114 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27117 node.parentNode.removeChild(node);
27118 return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
27123 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27124 node.parentNode.removeChild(node);
27125 return false; // dont do chidlren
27127 //Roo.log(node.tagName);
27128 // remove - but keep children..
27129 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27130 //Roo.log('-- removed');
27131 while (node.childNodes.length) {
27132 var cn = node.childNodes[0];
27133 node.removeChild(cn);
27134 node.parentNode.insertBefore(cn, node);
27135 // move node to parent - and clean it..
27136 if (cn.nodeType == 1) {
27137 this.replaceTag(cn);
27141 node.parentNode.removeChild(node);
27142 /// no need to iterate chidlren = it's got none..
27143 //this.iterateChildren(node, this.cleanWord);
27144 return false; // no need to iterate children.
27147 if (node.className.length) {
27149 var cn = node.className.split(/\W+/);
27151 Roo.each(cn, function(cls) {
27152 if (cls.match(/Mso[a-zA-Z]+/)) {
27157 node.className = cna.length ? cna.join(' ') : '';
27159 node.removeAttribute("class");
27163 if (node.hasAttribute("lang")) {
27164 node.removeAttribute("lang");
27167 if (node.hasAttribute("style")) {
27169 var styles = node.getAttribute("style").split(";");
27171 Roo.each(styles, function(s) {
27172 if (!s.match(/:/)) {
27175 var kv = s.split(":");
27176 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27179 // what ever is left... we allow.
27182 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27183 if (!nstyle.length) {
27184 node.removeAttribute('style');
27187 return true; // do children
27193 styleToObject: function(node)
27195 var styles = (node.getAttribute("style") || '').split(";");
27197 Roo.each(styles, function(s) {
27198 if (!s.match(/:/)) {
27201 var kv = s.split(":");
27203 // what ever is left... we allow.
27204 ret[kv[0].trim()] = kv[1];
27210 replaceAname : function (doc)
27212 // replace all the a/name without..
27213 var aa = Array.from(doc.getElementsByTagName('a'));
27214 for (var i = 0; i < aa.length; i++) {
27216 if (a.hasAttribute("name")) {
27217 a.removeAttribute("name");
27219 if (a.hasAttribute("href")) {
27222 // reparent children.
27223 this.removeNodeKeepChildren(a);
27233 replaceDocBullets : function(doc)
27235 // this is a bit odd - but it appears some indents use ql-indent-1
27236 //Roo.log(doc.innerHTML);
27238 var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
27239 for( var i = 0; i < listpara.length; i ++) {
27240 listpara[i].className = "MsoListParagraph";
27243 listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
27244 for( var i = 0; i < listpara.length; i ++) {
27245 listpara[i].className = "MsoListParagraph";
27247 listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
27248 for( var i = 0; i < listpara.length; i ++) {
27249 listpara[i].className = "MsoListParagraph";
27251 listpara = Array.from(doc.getElementsByClassName('ql-indent-1'));
27252 for( var i = 0; i < listpara.length; i ++) {
27253 listpara[i].className = "MsoListParagraph";
27256 // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
27257 var htwo = Array.from(doc.getElementsByTagName('h2'));
27258 for( var i = 0; i < htwo.length; i ++) {
27259 if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
27260 htwo[i].className = "MsoListParagraph";
27263 listpara = Array.from(doc.getElementsByClassName('MsoNormal'));
27264 for( var i = 0; i < listpara.length; i ++) {
27265 if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
27266 listpara[i].className = "MsoListParagraph";
27268 listpara[i].className = "MsoNormalx";
27272 listpara = doc.getElementsByClassName('MsoListParagraph');
27273 // Roo.log(doc.innerHTML);
27277 while(listpara.length) {
27279 this.replaceDocBullet(listpara.item(0));
27286 replaceDocBullet : function(p)
27288 // gather all the siblings.
27290 parent = p.parentNode,
27291 doc = parent.ownerDocument,
27294 //Roo.log("Parsing: " + p.innerText) ;
27295 var listtype = 'ul';
27297 if (ns.nodeType != 1) {
27298 ns = ns.nextSibling;
27301 if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
27302 //Roo.log("Missing para r q1indent - got:" + ns.className);
27305 var spans = ns.getElementsByTagName('span');
27307 if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
27309 ns = ns.nextSibling;
27311 if (!spans.length) {
27316 for (var i = 0; i < spans.length;i++) {
27318 if (se.hasAttribute('style') && se.hasAttribute('style') && se.style.fontFamily != '') {
27319 ff = se.style.fontFamily;
27325 //Roo.log("got font family: " + ff);
27326 if (typeof(ff) != 'undefined' && !ff.match(/(Symbol|Wingdings)/) && "·o".indexOf(se.innerText.trim()) < 0) {
27332 //Roo.log("no mso-list?");
27334 var spans = ns.getElementsByTagName('span');
27335 if (!spans.length) {
27338 var has_list = false;
27339 for(var i = 0; i < spans.length; i++) {
27340 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
27349 ns = ns.nextSibling;
27353 if (!items.length) {
27358 var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
27359 parent.insertBefore(ul, p);
27361 var stack = [ ul ];
27362 var last_li = false;
27364 var margin_to_depth = {};
27367 items.forEach(function(n, ipos) {
27368 //Roo.log("got innertHMLT=" + n.innerHTML);
27370 var spans = n.getElementsByTagName('span');
27371 if (!spans.length) {
27372 //Roo.log("No spans found");
27374 parent.removeChild(n);
27377 return; // skip it...
27383 for(var i = 0; i < spans.length; i++) {
27385 style = this.styleToObject(spans[i]);
27386 if (typeof(style['mso-list']) == 'undefined') {
27389 if (listtype == 'ol') {
27390 num = spans[i].innerText.replace(/[^0-9]+]/g,'') * 1;
27392 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
27395 //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
27396 style = this.styleToObject(n); // mo-list is from the parent node.
27397 if (typeof(style['mso-list']) == 'undefined') {
27398 //Roo.log("parent is missing level");
27400 parent.removeChild(n);
27405 var margin = style['margin-left'];
27406 if (typeof(margin_to_depth[margin]) == 'undefined') {
27408 margin_to_depth[margin] = max_margins;
27410 nlvl = margin_to_depth[margin] ;
27414 var nul = doc.createElement(listtype); // what about number lists...
27416 last_li = doc.createElement('li');
27417 stack[lvl].appendChild(last_li);
27419 last_li.appendChild(nul);
27425 // not starting at 1..
27426 if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
27427 stack[nlvl].setAttribute("start", num);
27430 var nli = stack[nlvl].appendChild(doc.createElement('li'));
27432 nli.innerHTML = n.innerHTML;
27433 //Roo.log("innerHTML = " + n.innerHTML);
27434 parent.removeChild(n);
27446 replaceImageTable : function(doc)
27449 <table cellpadding=0 cellspacing=0 align=left>
27451 <td width=423 height=0></td>
27455 <td><img width=601 height=401
27456 src="file:///C:/Users/Alan/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg"
27457 v:shapes="Picture_x0020_2"></td>
27461 var imgs = Array.from(doc.getElementsByTagName('img'));
27462 Roo.each(imgs, function(img) {
27463 var td = img.parentNode;
27464 if (td.nodeName != 'TD') {
27467 var tr = td.parentNode;
27468 if (tr.nodeName != 'TR') {
27471 var tbody = tr.parentNode;
27472 if (tbody.nodeName != 'TBODY') {
27475 var table = tbody.parentNode;
27476 if (table.nodeName != 'TABLE') {
27481 if (table.getElementsByTagName('tr').length != 2) {
27484 if (table.getElementsByTagName('td').length != 3) {
27487 if (table.innerText.trim() != '') {
27490 var p = table.parentNode;
27491 img.parentNode.removeChild(img);
27492 p.insertBefore(img, table);
27493 p.removeChild(table);
27504 * @class Roo.htmleditor.FilterStyleToTag
27505 * part of the word stuff... - certain 'styles' should be converted to tags.
27507 * font-weight: bold -> bold
27508 * ?? super / subscrit etc..
27511 * Run a new style to tag filter.
27512 * @param {Object} config Configuration options
27514 Roo.htmleditor.FilterStyleToTag = function(cfg)
27518 B : [ 'fontWeight' , 'bold'],
27519 I : [ 'fontStyle' , 'italic'],
27520 //pre : [ 'font-style' , 'italic'],
27521 // h1.. h6 ?? font-size?
27522 SUP : [ 'verticalAlign' , 'super' ],
27523 SUB : [ 'verticalAlign' , 'sub' ]
27528 Roo.apply(this, cfg);
27531 this.walk(cfg.node);
27538 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
27540 tag: true, // all tags
27545 replaceTag : function(node)
27549 if (node.getAttribute("style") === null) {
27553 for (var k in this.tags) {
27554 if (node.style[this.tags[k][0]] == this.tags[k][1]) {
27556 node.style.removeProperty(this.tags[k][0]);
27559 if (!inject.length) {
27562 var cn = Array.from(node.childNodes);
27564 Roo.each(inject, function(t) {
27565 var nc = node.ownerDocument.createElement(t);
27566 nn.appendChild(nc);
27569 for(var i = 0;i < cn.length;cn++) {
27570 node.removeChild(cn[i]);
27571 nn.appendChild(cn[i]);
27573 return true /// iterate thru
27577 * @class Roo.htmleditor.FilterLongBr
27578 * BR/BR/BR - keep a maximum of 2...
27580 * Run a new Long BR Filter
27581 * @param {Object} config Configuration options
27584 Roo.htmleditor.FilterLongBr = function(cfg)
27586 // no need to apply config.
27587 this.walk(cfg.node);
27590 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
27597 replaceTag : function(node)
27600 var ps = node.nextSibling;
27601 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27602 ps = ps.nextSibling;
27605 if (!ps && [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) {
27606 node.parentNode.removeChild(node); // remove last BR inside one fo these tags
27610 if (!ps || ps.nodeType != 1) {
27614 if (!ps || ps.tagName != 'BR') {
27623 if (!node.previousSibling) {
27626 var ps = node.previousSibling;
27628 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27629 ps = ps.previousSibling;
27631 if (!ps || ps.nodeType != 1) {
27634 // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
27635 if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
27639 node.parentNode.removeChild(node); // remove me...
27641 return false; // no need to do children
27648 * @class Roo.htmleditor.FilterBlock
27649 * removes id / data-block and contenteditable that are associated with blocks
27650 * usage should be done on a cloned copy of the dom
27652 * Run a new Attribute Filter { node : xxxx }}
27653 * @param {Object} config Configuration options
27655 Roo.htmleditor.FilterBlock = function(cfg)
27657 Roo.apply(this, cfg);
27658 var qa = cfg.node.querySelectorAll;
27659 this.removeAttributes('data-block');
27660 this.removeAttributes('contenteditable');
27661 this.removeAttributes('id');
27665 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27667 node: true, // all tags
27670 removeAttributes : function(attr)
27672 var ar = this.node.querySelectorAll('*[' + attr + ']');
27673 for (var i =0;i<ar.length;i++) {
27674 ar[i].removeAttribute(attr);
27683 * This is based loosely on tinymce
27684 * @class Roo.htmleditor.TidySerializer
27685 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27687 * @method Serializer
27688 * @param {Object} settings Name/value settings object.
27692 Roo.htmleditor.TidySerializer = function(settings)
27694 Roo.apply(this, settings);
27696 this.writer = new Roo.htmleditor.TidyWriter(settings);
27701 Roo.htmleditor.TidySerializer.prototype = {
27704 * @param {boolean} inner do the inner of the node.
27711 * Serializes the specified node into a string.
27714 * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27715 * @method serialize
27716 * @param {DomElement} node Node instance to serialize.
27717 * @return {String} String with HTML based on DOM tree.
27719 serialize : function(node) {
27721 // = settings.validate;
27722 var writer = this.writer;
27726 3: function(node) {
27728 writer.text(node.nodeValue, node);
27731 8: function(node) {
27732 writer.comment(node.nodeValue);
27734 // Processing instruction
27735 7: function(node) {
27736 writer.pi(node.name, node.nodeValue);
27739 10: function(node) {
27740 writer.doctype(node.nodeValue);
27743 4: function(node) {
27744 writer.cdata(node.nodeValue);
27746 // Document fragment
27747 11: function(node) {
27748 node = node.firstChild;
27754 node = node.nextSibling
27759 1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27760 return writer.getContent();
27763 walk: function(node)
27765 var attrName, attrValue, sortedAttrs, i, l, elementRule,
27766 handler = this.handlers[node.nodeType];
27773 var name = node.nodeName;
27774 var isEmpty = node.childNodes.length < 1;
27776 var writer = this.writer;
27777 var attrs = node.attributes;
27780 writer.start(node.nodeName, attrs, isEmpty, node);
27784 node = node.firstChild;
27791 node = node.nextSibling;
27797 // Serialize element and treat all non elements as fragments
27802 * This is based loosely on tinymce
27803 * @class Roo.htmleditor.TidyWriter
27804 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27807 * - not tested much with 'PRE' formated elements.
27813 Roo.htmleditor.TidyWriter = function(settings)
27816 // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27817 Roo.apply(this, settings);
27821 this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27824 Roo.htmleditor.TidyWriter.prototype = {
27831 // part of state...
27835 last_inline : false,
27840 * Writes the a start element such as <p id="a">.
27843 * @param {String} name Name of the element.
27844 * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27845 * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27847 start: function(name, attrs, empty, node)
27849 var i, l, attr, value;
27851 // there are some situations where adding line break && indentation will not work. will not work.
27852 // <span / b / i ... formating?
27854 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27855 var in_pre = this.in_pre || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27857 var is_short = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27859 var add_lb = name == 'BR' ? false : in_inline;
27861 if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27865 var indentstr = this.indentstr;
27867 // e_inline = elements that can be inline, but still allow \n before and after?
27868 // only 'BR' ??? any others?
27870 // ADD LINE BEFORE tage
27871 if (!this.in_pre) {
27874 if (name == 'BR') {
27876 } else if (this.lastElementEndsWS()) {
27879 // otherwise - no new line. (and dont indent.)
27890 this.html.push(indentstr + '<', name.toLowerCase());
27893 for (i = 0, l = attrs.length; i < l; i++) {
27895 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27901 this.html[this.html.length] = '/>';
27903 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27905 var e_inline = name == 'BR' ? false : this.in_inline;
27907 if (!e_inline && !this.in_pre) {
27914 this.html[this.html.length] = '>';
27916 // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27918 if (!in_inline && !in_pre) {
27919 var cn = node.firstChild;
27921 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27925 cn = cn.nextSibling;
27933 indentstr : in_pre ? '' : (this.indentstr + this.indent),
27935 in_inline : in_inline
27937 // add a line after if we are not in a
27939 if (!in_inline && !in_pre) {
27948 lastElementEndsWS : function()
27950 var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27951 if (value === false) {
27954 return value.match(/\s+$/);
27959 * Writes the a end element such as </p>.
27962 * @param {String} name Name of the element.
27964 end: function(name) {
27967 var indentstr = '';
27968 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27970 if (!this.in_pre && !in_inline) {
27972 indentstr = this.indentstr;
27974 this.html.push(indentstr + '</', name.toLowerCase(), '>');
27975 this.last_inline = in_inline;
27977 // pop the indent state..
27980 * Writes a text node.
27982 * In pre - we should not mess with the contents.
27986 * @param {String} text String to write out.
27987 * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27989 text: function(in_text, node)
27991 // if not in whitespace critical
27992 if (in_text.length < 1) {
27995 var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27998 this.html[this.html.length] = text;
28002 if (this.in_inline) {
28003 text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
28005 text = text.replace(/\s+/,' '); // all white space to single white space
28008 // if next tag is '<BR>', then we can trim right..
28009 if (node.nextSibling &&
28010 node.nextSibling.nodeType == 1 &&
28011 node.nextSibling.nodeName == 'BR' )
28013 text = text.replace(/\s+$/g,'');
28015 // if previous tag was a BR, we can also trim..
28016 if (node.previousSibling &&
28017 node.previousSibling.nodeType == 1 &&
28018 node.previousSibling.nodeName == 'BR' )
28020 text = this.indentstr + text.replace(/^\s+/g,'');
28022 if (text.match(/\n/)) {
28023 text = text.replace(
28024 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
28026 // remoeve the last whitespace / line break.
28027 text = text.replace(/\n\s+$/,'');
28029 // repace long lines
28033 this.html[this.html.length] = text;
28036 // see if previous element was a inline element.
28037 var indentstr = this.indentstr;
28039 text = text.replace(/\s+/g," "); // all whitespace into single white space.
28041 // should trim left?
28042 if (node.previousSibling &&
28043 node.previousSibling.nodeType == 1 &&
28044 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
28050 text = text.replace(/^\s+/,''); // trim left
28053 // should trim right?
28054 if (node.nextSibling &&
28055 node.nextSibling.nodeType == 1 &&
28056 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
28061 text = text.replace(/\s+$/,''); // trim right
28068 if (text.length < 1) {
28071 if (!text.match(/\n/)) {
28072 this.html.push(indentstr + text);
28076 text = this.indentstr + text.replace(
28077 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
28079 // remoeve the last whitespace / line break.
28080 text = text.replace(/\s+$/,'');
28082 this.html.push(text);
28084 // split and indent..
28089 * Writes a cdata node such as <![CDATA[data]]>.
28092 * @param {String} text String to write out inside the cdata.
28094 cdata: function(text) {
28095 this.html.push('<![CDATA[', text, ']]>');
28098 * Writes a comment node such as <!-- Comment -->.
28101 * @param {String} text String to write out inside the comment.
28103 comment: function(text) {
28104 this.html.push('<!--', text, '-->');
28107 * Writes a PI node such as <?xml attr="value" ?>.
28110 * @param {String} name Name of the pi.
28111 * @param {String} text String to write out inside the pi.
28113 pi: function(name, text) {
28114 text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
28115 this.indent != '' && this.html.push('\n');
28118 * Writes a doctype node such as <!DOCTYPE data>.
28121 * @param {String} text String to write out inside the doctype.
28123 doctype: function(text) {
28124 this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
28127 * Resets the internal buffer if one wants to reuse the writer.
28131 reset: function() {
28132 this.html.length = 0;
28141 * Returns the contents that got serialized.
28143 * @method getContent
28144 * @return {String} HTML contents that got written down.
28146 getContent: function() {
28147 return this.html.join('').replace(/\n$/, '');
28150 pushState : function(cfg)
28152 this.state.push(cfg);
28153 Roo.apply(this, cfg);
28156 popState : function()
28158 if (this.state.length < 1) {
28159 return; // nothing to push
28166 if (this.state.length > 0) {
28167 cfg = this.state[this.state.length-1];
28169 Roo.apply(this, cfg);
28172 addLine: function()
28174 if (this.html.length < 1) {
28179 var value = this.html[this.html.length - 1];
28180 if (value.length > 0 && '\n' !== value) {
28181 this.html.push('\n');
28186 //'pre script noscript style textarea video audio iframe object code'
28187 // shortended... 'area base basefont br col frame hr img input isindex link meta param embed source wbr track');
28191 Roo.htmleditor.TidyWriter.inline_elements = [
28192 'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
28193 'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
28195 Roo.htmleditor.TidyWriter.shortend_elements = [
28196 'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
28197 'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
28200 Roo.htmleditor.TidyWriter.whitespace_elements = [
28201 'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
28203 * This is based loosely on tinymce
28204 * @class Roo.htmleditor.TidyEntities
28206 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
28208 * Not 100% sure this is actually used or needed.
28211 Roo.htmleditor.TidyEntities = {
28214 * initialize data..
28216 init : function (){
28218 this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
28223 buildEntitiesLookup: function(items, radix) {
28224 var i, chr, entity, lookup = {};
28228 items = typeof(items) == 'string' ? items.split(',') : items;
28229 radix = radix || 10;
28230 // Build entities lookup table
28231 for (i = 0; i < items.length; i += 2) {
28232 chr = String.fromCharCode(parseInt(items[i], radix));
28233 // Only add non base entities
28234 if (!this.baseEntities[chr]) {
28235 entity = '&' + items[i + 1] + ';';
28236 lookup[chr] = entity;
28237 lookup[entity] = chr;
28276 // Needs to be escaped since the YUI compressor would otherwise break the code
28283 // Reverse lookup table for raw entities
28284 reverseEntities : {
28292 attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28293 textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28294 rawCharsRegExp : /[<>&\"\']/g,
28295 entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
28296 namedEntities : false,
28297 namedEntitiesData : [
28798 * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28800 * @method encodeRaw
28801 * @param {String} text Text to encode.
28802 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28803 * @return {String} Entity encoded text.
28805 encodeRaw: function(text, attr)
28808 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28809 return t.baseEntities[chr] || chr;
28813 * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28814 * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28815 * and is exposed as the DOMUtils.encode function.
28817 * @method encodeAllRaw
28818 * @param {String} text Text to encode.
28819 * @return {String} Entity encoded text.
28821 encodeAllRaw: function(text) {
28823 return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28824 return t.baseEntities[chr] || chr;
28828 * Encodes the specified string using numeric entities. The core entities will be
28829 * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28831 * @method encodeNumeric
28832 * @param {String} text Text to encode.
28833 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28834 * @return {String} Entity encoded text.
28836 encodeNumeric: function(text, attr) {
28838 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28839 // Multi byte sequence convert it to a single entity
28840 if (chr.length > 1) {
28841 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28843 return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28847 * Encodes the specified string using named entities. The core entities will be encoded
28848 * as named ones but all non lower ascii characters will be encoded into named entities.
28850 * @method encodeNamed
28851 * @param {String} text Text to encode.
28852 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28853 * @param {Object} entities Optional parameter with entities to use.
28854 * @return {String} Entity encoded text.
28856 encodeNamed: function(text, attr, entities) {
28858 entities = entities || this.namedEntities;
28859 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28860 return t.baseEntities[chr] || entities[chr] || chr;
28864 * Returns an encode function based on the name(s) and it's optional entities.
28866 * @method getEncodeFunc
28867 * @param {String} name Comma separated list of encoders for example named,numeric.
28868 * @param {String} entities Optional parameter with entities to use instead of the built in set.
28869 * @return {function} Encode function to be used.
28871 getEncodeFunc: function(name, entities) {
28872 entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28874 function encodeNamedAndNumeric(text, attr) {
28875 return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28876 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28880 function encodeCustomNamed(text, attr) {
28881 return t.encodeNamed(text, attr, entities);
28883 // Replace + with , to be compatible with previous TinyMCE versions
28884 name = this.makeMap(name.replace(/\+/g, ','));
28885 // Named and numeric encoder
28886 if (name.named && name.numeric) {
28887 return this.encodeNamedAndNumeric;
28893 return encodeCustomNamed;
28895 return this.encodeNamed;
28898 if (name.numeric) {
28899 return this.encodeNumeric;
28902 return this.encodeRaw;
28905 * Decodes the specified string, this will replace entities with raw UTF characters.
28908 * @param {String} text Text to entity decode.
28909 * @return {String} Entity decoded string.
28911 decode: function(text)
28914 return text.replace(this.entityRegExp, function(all, numeric) {
28916 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28917 // Support upper UTF
28918 if (numeric > 65535) {
28920 return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28922 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28924 return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28927 nativeDecode : function (text) {
28930 makeMap : function (items, delim, map) {
28932 items = items || [];
28933 delim = delim || ',';
28934 if (typeof items == "string") {
28935 items = items.split(delim);
28940 map[items[i]] = {};
28948 Roo.htmleditor.TidyEntities.init();
28950 * @class Roo.htmleditor.KeyEnter
28951 * Handle Enter press..
28952 * @cfg {Roo.HtmlEditorCore} core the editor.
28954 * Create a new Filter.
28955 * @param {Object} config Configuration options
28962 Roo.htmleditor.KeyEnter = function(cfg) {
28963 Roo.apply(this, cfg);
28964 // this does not actually call walk as it's really just a abstract class
28966 Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28969 //Roo.htmleditor.KeyEnter.i = 0;
28972 Roo.htmleditor.KeyEnter.prototype = {
28976 keypress : function(e)
28978 if (e.charCode != 13 && e.charCode != 10) {
28979 Roo.log([e.charCode,e]);
28982 e.preventDefault();
28983 // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28984 var doc = this.core.doc;
28988 var sel = this.core.getSelection();
28989 var range = sel.getRangeAt(0);
28990 var n = range.commonAncestorContainer;
28991 var pc = range.closest([ 'ol', 'ul']);
28992 var pli = range.closest('li');
28993 if (!pc || e.ctrlKey) {
28994 // on it list, or ctrl pressed.
28996 sel.insertNode('br', 'after');
28998 // only do this if we have ctrl key..
28999 var br = doc.createElement('br');
29000 br.className = 'clear';
29001 br.setAttribute('style', 'clear: both');
29002 sel.insertNode(br, 'after');
29006 this.core.undoManager.addEvent();
29007 this.core.fireEditorEvent(e);
29011 // deal with <li> insetion
29012 if (pli.innerText.trim() == '' &&
29013 pli.previousSibling &&
29014 pli.previousSibling.nodeName == 'LI' &&
29015 pli.previousSibling.innerText.trim() == '') {
29016 pli.parentNode.removeChild(pli.previousSibling);
29017 sel.cursorAfter(pc);
29018 this.core.undoManager.addEvent();
29019 this.core.fireEditorEvent(e);
29023 var li = doc.createElement('LI');
29024 li.innerHTML = ' ';
29025 if (!pli || !pli.firstSibling) {
29026 pc.appendChild(li);
29028 pli.parentNode.insertBefore(li, pli.firstSibling);
29030 sel.cursorText (li.firstChild);
29032 this.core.undoManager.addEvent();
29033 this.core.fireEditorEvent(e);
29045 * @class Roo.htmleditor.Block
29046 * Base class for html editor blocks - do not use it directly .. extend it..
29047 * @cfg {DomElement} node The node to apply stuff to.
29048 * @cfg {String} friendly_name the name that appears in the context bar about this block
29049 * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
29052 * Create a new Filter.
29053 * @param {Object} config Configuration options
29056 Roo.htmleditor.Block = function(cfg)
29058 // do nothing .. should not be called really.
29061 * factory method to get the block from an element (using cache if necessary)
29063 * @param {HtmlElement} the dom element
29065 Roo.htmleditor.Block.factory = function(node)
29067 var cc = Roo.htmleditor.Block.cache;
29068 var id = Roo.get(node).id;
29069 if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
29070 Roo.htmleditor.Block.cache[id].readElement(node);
29071 return Roo.htmleditor.Block.cache[id];
29073 var db = node.getAttribute('data-block');
29075 db = node.nodeName.toLowerCase().toUpperCaseFirst();
29077 var cls = Roo.htmleditor['Block' + db];
29078 if (typeof(cls) == 'undefined') {
29079 //Roo.log(node.getAttribute('data-block'));
29080 Roo.log("OOps missing block : " + 'Block' + db);
29083 Roo.htmleditor.Block.cache[id] = new cls({ node: node });
29084 return Roo.htmleditor.Block.cache[id]; /// should trigger update element
29088 * initalize all Elements from content that are 'blockable'
29090 * @param the body element
29092 Roo.htmleditor.Block.initAll = function(body, type)
29094 if (typeof(type) == 'undefined') {
29095 var ia = Roo.htmleditor.Block.initAll;
29101 Roo.each(Roo.get(body).query(type), function(e) {
29102 Roo.htmleditor.Block.factory(e);
29105 // question goes here... do we need to clear out this cache sometimes?
29106 // or show we make it relivant to the htmleditor.
29107 Roo.htmleditor.Block.cache = {};
29109 Roo.htmleditor.Block.prototype = {
29113 // used by context menu
29114 friendly_name : 'Based Block',
29116 // text for button to delete this element
29117 deleteTitle : false,
29121 * Update a node with values from this object
29122 * @param {DomElement} node
29124 updateElement : function(node)
29126 Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
29129 * convert to plain HTML for calling insertAtCursor..
29131 toHTML : function()
29133 return Roo.DomHelper.markup(this.toObject());
29136 * used by readEleemnt to extract data from a node
29137 * may need improving as it's pretty basic
29139 * @param {DomElement} node
29140 * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
29141 * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
29142 * @param {String} style the style property - eg. text-align
29144 getVal : function(node, tag, attr, style)
29147 if (tag !== true && n.tagName != tag.toUpperCase()) {
29148 // in theory we could do figure[3] << 3rd figure? or some more complex search..?
29149 // but kiss for now.
29150 n = node.getElementsByTagName(tag).item(0);
29155 if (attr === false) {
29158 if (attr == 'html') {
29159 return n.innerHTML;
29161 if (attr == 'style') {
29162 return n.style[style];
29165 return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
29169 * create a DomHelper friendly object - for use with
29170 * Roo.DomHelper.markup / overwrite / etc..
29173 toObject : function()
29178 * Read a node that has a 'data-block' property - and extract the values from it.
29179 * @param {DomElement} node - the node
29181 readElement : function(node)
29192 * @class Roo.htmleditor.BlockFigure
29193 * Block that has an image and a figcaption
29194 * @cfg {String} image_src the url for the image
29195 * @cfg {String} align (left|right) alignment for the block default left
29196 * @cfg {String} caption the text to appear below (and in the alt tag)
29197 * @cfg {String} caption_display (block|none) display or not the caption
29198 * @cfg {String|number} image_width the width of the image number or %?
29199 * @cfg {String|number} image_height the height of the image number or %?
29202 * Create a new Filter.
29203 * @param {Object} config Configuration options
29206 Roo.htmleditor.BlockFigure = function(cfg)
29209 this.readElement(cfg.node);
29210 this.updateElement(cfg.node);
29212 Roo.apply(this, cfg);
29214 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
29221 caption_display : 'block',
29227 // margin: '2%', not used
29229 text_align: 'left', // (left|right) alignment for the text caption default left. - not used at present
29232 // used by context menu
29233 friendly_name : 'Image with caption',
29234 deleteTitle : "Delete Image and Caption",
29236 contextMenu : function(toolbar)
29239 var block = function() {
29240 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29244 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29246 var syncValue = toolbar.editorcore.syncValue;
29252 xtype : 'TextItem',
29254 xns : rooui.Toolbar //Boostrap?
29258 text: 'Change Image URL',
29261 click: function (btn, state)
29265 Roo.MessageBox.show({
29266 title : "Image Source URL",
29267 msg : "Enter the url for the image",
29268 buttons: Roo.MessageBox.OKCANCEL,
29269 fn: function(btn, val){
29276 toolbar.editorcore.onEditorEvent();
29280 //multiline: multiline,
29282 value : b.image_src
29286 xns : rooui.Toolbar
29291 text: 'Change Link URL',
29294 click: function (btn, state)
29298 Roo.MessageBox.show({
29299 title : "Link URL",
29300 msg : "Enter the url for the link - leave blank to have no link",
29301 buttons: Roo.MessageBox.OKCANCEL,
29302 fn: function(btn, val){
29309 toolbar.editorcore.onEditorEvent();
29313 //multiline: multiline,
29319 xns : rooui.Toolbar
29323 text: 'Show Video URL',
29326 click: function (btn, state)
29328 Roo.MessageBox.alert("Video URL",
29329 block().video_url == '' ? 'This image is not linked ot a video' :
29330 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
29333 xns : rooui.Toolbar
29338 xtype : 'TextItem',
29340 xns : rooui.Toolbar //Boostrap?
29343 xtype : 'ComboBox',
29344 allowBlank : false,
29345 displayField : 'val',
29348 triggerAction : 'all',
29350 valueField : 'val',
29354 select : function (combo, r, index)
29356 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29358 b.width = r.get('val');
29361 toolbar.editorcore.onEditorEvent();
29366 xtype : 'SimpleStore',
29379 xtype : 'TextItem',
29381 xns : rooui.Toolbar //Boostrap?
29384 xtype : 'ComboBox',
29385 allowBlank : false,
29386 displayField : 'val',
29389 triggerAction : 'all',
29391 valueField : 'val',
29395 select : function (combo, r, index)
29397 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29399 b.align = r.get('val');
29402 toolbar.editorcore.onEditorEvent();
29407 xtype : 'SimpleStore',
29421 text: 'Hide Caption',
29422 name : 'caption_display',
29424 enableToggle : true,
29425 setValue : function(v) {
29426 // this trigger toggle.
29428 this.setText(v ? "Hide Caption" : "Show Caption");
29429 this.setPressed(v != 'block');
29432 toggle: function (btn, state)
29435 b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
29436 this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
29439 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29440 toolbar.editorcore.onEditorEvent();
29443 xns : rooui.Toolbar
29449 * create a DomHelper friendly object - for use with
29450 * Roo.DomHelper.markup / overwrite / etc..
29452 toObject : function()
29454 var d = document.createElement('div');
29455 d.innerHTML = this.caption;
29457 var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0;
29459 var iw = this.align == 'center' ? this.width : '100%';
29462 contenteditable : 'false',
29463 src : this.image_src,
29464 alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
29467 maxWidth : iw + ' !important', // this is not getting rendered?
29473 '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
29475 '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' +
29480 if (this.href.length > 0) {
29484 contenteditable : 'true',
29492 if (this.video_url.length > 0) {
29497 allowfullscreen : true,
29498 width : 420, // these are for video tricks - that we replace the outer
29500 src : this.video_url,
29506 // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
29507 var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
29512 'data-block' : 'Figure',
29513 'data-width' : this.width,
29514 'data-caption' : this.caption,
29515 contenteditable : 'false',
29519 float : this.align ,
29520 maxWidth : this.align == 'center' ? '100% !important' : (this.width + ' !important'),
29521 width : this.align == 'center' ? '100%' : this.width,
29523 padding: this.align == 'center' ? '0' : '0 10px' ,
29524 textAlign : this.align // seems to work for email..
29529 align : this.align,
29535 'data-display' : this.caption_display,
29537 textAlign : 'left',
29539 lineHeight : '24px',
29540 display : this.caption_display,
29541 maxWidth : (this.align == 'center' ? this.width : '100%' ) + ' !important',
29543 width: this.align == 'center' ? this.width : '100%'
29547 cls : this.cls.length > 0 ? (this.cls + '-thumbnail' ) : '',
29552 marginTop : '16px',
29558 // we can not rely on yahoo syndication to use CSS elements - so have to use '<i>' to encase stuff.
29560 contenteditable : true,
29576 readElement : function(node)
29578 // this should not really come from the link...
29579 this.video_url = this.getVal(node, 'div', 'src');
29580 this.cls = this.getVal(node, 'div', 'class');
29581 this.href = this.getVal(node, 'a', 'href');
29584 this.image_src = this.getVal(node, 'img', 'src');
29586 this.align = this.getVal(node, 'figure', 'align');
29588 /// not really used - as hidden captions do not store the content here..
29589 var figcaption = this.getVal(node, 'figcaption', false);
29590 if (figcaption !== '') {
29591 this.caption = this.getVal(figcaption, 'i', 'html');
29595 this.caption_display = this.getVal(node, 'figcaption', 'data-display');
29596 var dc = this.getVal(node, true, 'data-caption');
29597 if (dc && dc.length) {
29600 //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
29601 this.width = this.getVal(node, true, 'data-width');
29602 //this.margin = this.getVal(node, 'figure', 'style', 'margin');
29605 removeNode : function()
29622 * @class Roo.htmleditor.BlockTable
29623 * Block that manages a table
29626 * Create a new Filter.
29627 * @param {Object} config Configuration options
29630 Roo.htmleditor.BlockTable = function(cfg)
29633 this.readElement(cfg.node);
29634 this.updateElement(cfg.node);
29636 Roo.apply(this, cfg);
29639 for(var r = 0; r < this.no_row; r++) {
29641 for(var c = 0; c < this.no_col; c++) {
29642 this.rows[r][c] = this.emptyCell();
29649 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
29658 // used by context menu
29659 friendly_name : 'Table',
29660 deleteTitle : 'Delete Table',
29661 // context menu is drawn once..
29663 contextMenu : function(toolbar)
29666 var block = function() {
29667 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29671 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29673 var syncValue = toolbar.editorcore.syncValue;
29679 xtype : 'TextItem',
29681 xns : rooui.Toolbar //Boostrap?
29684 xtype : 'ComboBox',
29685 allowBlank : false,
29686 displayField : 'val',
29689 triggerAction : 'all',
29691 valueField : 'val',
29695 select : function (combo, r, index)
29697 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29699 b.width = r.get('val');
29702 toolbar.editorcore.onEditorEvent();
29707 xtype : 'SimpleStore',
29719 xtype : 'TextItem',
29720 text : "Columns: ",
29721 xns : rooui.Toolbar //Boostrap?
29728 click : function (_self, e)
29730 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29731 block().removeColumn();
29733 toolbar.editorcore.onEditorEvent();
29736 xns : rooui.Toolbar
29742 click : function (_self, e)
29744 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29745 block().addColumn();
29747 toolbar.editorcore.onEditorEvent();
29750 xns : rooui.Toolbar
29754 xtype : 'TextItem',
29756 xns : rooui.Toolbar //Boostrap?
29763 click : function (_self, e)
29765 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29766 block().removeRow();
29768 toolbar.editorcore.onEditorEvent();
29771 xns : rooui.Toolbar
29777 click : function (_self, e)
29781 toolbar.editorcore.onEditorEvent();
29784 xns : rooui.Toolbar
29789 text: 'Reset Column Widths',
29792 click : function (_self, e)
29794 block().resetWidths();
29796 toolbar.editorcore.onEditorEvent();
29799 xns : rooui.Toolbar
29810 * create a DomHelper friendly object - for use with
29811 * Roo.DomHelper.markup / overwrite / etc..
29812 * ?? should it be called with option to hide all editing features?
29814 toObject : function()
29819 contenteditable : 'false', // this stops cell selection from picking the table.
29820 'data-block' : 'Table',
29823 border : 'solid 1px #000', // ??? hard coded?
29824 'border-collapse' : 'collapse'
29827 { tag : 'tbody' , cn : [] }
29831 // do we have a head = not really
29833 Roo.each(this.rows, function( row ) {
29838 border : 'solid 1px #000',
29844 ret.cn[0].cn.push(tr);
29845 // does the row have any properties? ?? height?
29847 Roo.each(row, function( cell ) {
29851 contenteditable : 'true',
29852 'data-block' : 'Td',
29856 if (cell.colspan > 1) {
29857 td.colspan = cell.colspan ;
29858 nc += cell.colspan;
29862 if (cell.rowspan > 1) {
29863 td.rowspan = cell.rowspan ;
29872 ncols = Math.max(nc, ncols);
29876 // add the header row..
29885 readElement : function(node)
29887 node = node ? node : this.node ;
29888 this.width = this.getVal(node, true, 'style', 'width') || '100%';
29892 var trs = Array.from(node.rows);
29893 trs.forEach(function(tr) {
29895 this.rows.push(row);
29899 Array.from(tr.cells).forEach(function(td) {
29902 colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29903 rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29904 style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29905 html : td.innerHTML
29907 no_column += add.colspan;
29914 this.no_col = Math.max(this.no_col, no_column);
29921 normalizeRows: function()
29925 this.rows.forEach(function(row) {
29928 row = this.normalizeRow(row);
29930 row.forEach(function(c) {
29931 while (typeof(ret[rid][cid]) != 'undefined') {
29934 if (typeof(ret[rid]) == 'undefined') {
29940 if (c.rowspan < 2) {
29944 for(var i = 1 ;i < c.rowspan; i++) {
29945 if (typeof(ret[rid+i]) == 'undefined') {
29948 ret[rid+i][cid] = c;
29956 normalizeRow: function(row)
29959 row.forEach(function(c) {
29960 if (c.colspan < 2) {
29964 for(var i =0 ;i < c.colspan; i++) {
29972 deleteColumn : function(sel)
29974 if (!sel || sel.type != 'col') {
29977 if (this.no_col < 2) {
29981 this.rows.forEach(function(row) {
29982 var cols = this.normalizeRow(row);
29983 var col = cols[sel.col];
29984 if (col.colspan > 1) {
29994 removeColumn : function()
29996 this.deleteColumn({
29998 col : this.no_col-1
30000 this.updateElement();
30004 addColumn : function()
30007 this.rows.forEach(function(row) {
30008 row.push(this.emptyCell());
30011 this.updateElement();
30014 deleteRow : function(sel)
30016 if (!sel || sel.type != 'row') {
30020 if (this.no_row < 2) {
30024 var rows = this.normalizeRows();
30027 rows[sel.row].forEach(function(col) {
30028 if (col.rowspan > 1) {
30031 col.remove = 1; // flage it as removed.
30036 this.rows.forEach(function(row) {
30038 row.forEach(function(c) {
30039 if (typeof(c.remove) == 'undefined') {
30044 if (newrow.length > 0) {
30048 this.rows = newrows;
30053 this.updateElement();
30056 removeRow : function()
30060 row : this.no_row-1
30066 addRow : function()
30070 for (var i = 0; i < this.no_col; i++ ) {
30072 row.push(this.emptyCell());
30075 this.rows.push(row);
30076 this.updateElement();
30080 // the default cell object... at present...
30081 emptyCell : function() {
30082 return (new Roo.htmleditor.BlockTd({})).toObject();
30087 removeNode : function()
30094 resetWidths : function()
30096 Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
30097 var nn = Roo.htmleditor.Block.factory(n);
30099 nn.updateElement(n);
30112 * since selections really work on the table cell, then editing really should work from there
30114 * The original plan was to support merging etc... - but that may not be needed yet..
30116 * So this simple version will support:
30118 * adjust the width +/-
30119 * reset the width...
30128 * @class Roo.htmleditor.BlockTable
30129 * Block that manages a table
30132 * Create a new Filter.
30133 * @param {Object} config Configuration options
30136 Roo.htmleditor.BlockTd = function(cfg)
30139 this.readElement(cfg.node);
30140 this.updateElement(cfg.node);
30142 Roo.apply(this, cfg);
30147 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
30152 textAlign : 'left',
30159 // used by context menu
30160 friendly_name : 'Table Cell',
30161 deleteTitle : false, // use our customer delete
30163 // context menu is drawn once..
30165 contextMenu : function(toolbar)
30168 var cell = function() {
30169 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
30172 var table = function() {
30173 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
30177 var saveSel = function()
30179 lr = toolbar.editorcore.getSelection().getRangeAt(0);
30181 var restoreSel = function()
30185 toolbar.editorcore.focus();
30186 var cr = toolbar.editorcore.getSelection();
30187 cr.removeAllRanges();
30189 toolbar.editorcore.onEditorEvent();
30190 }).defer(10, this);
30196 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
30198 var syncValue = toolbar.editorcore.syncValue;
30205 text : 'Edit Table',
30207 click : function() {
30208 var t = toolbar.tb.selectedNode.closest('table');
30209 toolbar.editorcore.selectNode(t);
30210 toolbar.editorcore.onEditorEvent();
30219 xtype : 'TextItem',
30220 text : "Column Width: ",
30221 xns : rooui.Toolbar
30228 click : function (_self, e)
30230 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30231 cell().shrinkColumn();
30233 toolbar.editorcore.onEditorEvent();
30236 xns : rooui.Toolbar
30242 click : function (_self, e)
30244 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30245 cell().growColumn();
30247 toolbar.editorcore.onEditorEvent();
30250 xns : rooui.Toolbar
30254 xtype : 'TextItem',
30255 text : "Vertical Align: ",
30256 xns : rooui.Toolbar //Boostrap?
30259 xtype : 'ComboBox',
30260 allowBlank : false,
30261 displayField : 'val',
30264 triggerAction : 'all',
30266 valueField : 'val',
30270 select : function (combo, r, index)
30272 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30274 b.valign = r.get('val');
30277 toolbar.editorcore.onEditorEvent();
30282 xtype : 'SimpleStore',
30286 ['bottom'] // there are afew more...
30294 xtype : 'TextItem',
30295 text : "Merge Cells: ",
30296 xns : rooui.Toolbar
30305 click : function (_self, e)
30307 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30308 cell().mergeRight();
30309 //block().growColumn();
30311 toolbar.editorcore.onEditorEvent();
30314 xns : rooui.Toolbar
30321 click : function (_self, e)
30323 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30324 cell().mergeBelow();
30325 //block().growColumn();
30327 toolbar.editorcore.onEditorEvent();
30330 xns : rooui.Toolbar
30333 xtype : 'TextItem',
30335 xns : rooui.Toolbar
30343 click : function (_self, e)
30345 //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30348 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30349 toolbar.editorcore.onEditorEvent();
30353 xns : rooui.Toolbar
30357 xns : rooui.Toolbar
30366 xns : rooui.Toolbar,
30375 click : function (_self, e)
30379 cell().deleteColumn();
30381 toolbar.editorcore.selectNode(t.node);
30382 toolbar.editorcore.onEditorEvent();
30391 click : function (_self, e)
30394 cell().deleteRow();
30397 toolbar.editorcore.selectNode(t.node);
30398 toolbar.editorcore.onEditorEvent();
30405 xtype : 'Separator',
30412 click : function (_self, e)
30415 var nn = t.node.nextSibling || t.node.previousSibling;
30416 t.node.parentNode.removeChild(t.node);
30418 toolbar.editorcore.selectNode(nn, true);
30420 toolbar.editorcore.onEditorEvent();
30430 // align... << fixme
30438 * create a DomHelper friendly object - for use with
30439 * Roo.DomHelper.markup / overwrite / etc..
30440 * ?? should it be called with option to hide all editing features?
30443 * create a DomHelper friendly object - for use with
30444 * Roo.DomHelper.markup / overwrite / etc..
30445 * ?? should it be called with option to hide all editing features?
30447 toObject : function()
30451 contenteditable : 'true', // this stops cell selection from picking the table.
30452 'data-block' : 'Td',
30453 valign : this.valign,
30455 'text-align' : this.textAlign,
30456 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
30457 'border-collapse' : 'collapse',
30458 padding : '6px', // 8 for desktop / 4 for mobile
30459 'vertical-align': this.valign
30463 if (this.width != '') {
30464 ret.width = this.width;
30465 ret.style.width = this.width;
30469 if (this.colspan > 1) {
30470 ret.colspan = this.colspan ;
30472 if (this.rowspan > 1) {
30473 ret.rowspan = this.rowspan ;
30482 readElement : function(node)
30484 node = node ? node : this.node ;
30485 this.width = node.style.width;
30486 this.colspan = Math.max(1,1*node.getAttribute('colspan'));
30487 this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
30488 this.html = node.innerHTML;
30489 if (node.style.textAlign != '') {
30490 this.textAlign = node.style.textAlign;
30496 // the default cell object... at present...
30497 emptyCell : function() {
30501 textAlign : 'left',
30502 html : " " // is this going to be editable now?
30507 removeNode : function()
30509 return this.node.closest('table');
30517 toTableArray : function()
30520 var tab = this.node.closest('tr').closest('table');
30521 Array.from(tab.rows).forEach(function(r, ri){
30525 this.colWidths = [];
30526 var all_auto = true;
30527 Array.from(tab.rows).forEach(function(r, ri){
30530 Array.from(r.cells).forEach(function(ce, ci){
30535 colspan : ce.colSpan,
30536 rowspan : ce.rowSpan
30538 if (ce.isEqualNode(this.node)) {
30541 // if we have been filled up by a row?
30542 if (typeof(ret[rn][cn]) != 'undefined') {
30543 while(typeof(ret[rn][cn]) != 'undefined') {
30549 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
30550 this.colWidths[cn] = ce.style.width;
30551 if (this.colWidths[cn] != '') {
30557 if (c.colspan < 2 && c.rowspan < 2 ) {
30562 for(var j = 0; j < c.rowspan; j++) {
30563 if (typeof(ret[rn+j]) == 'undefined') {
30564 continue; // we have a problem..
30567 for(var i = 0; i < c.colspan; i++) {
30568 ret[rn+j][cn+i] = c;
30577 // initalize widths.?
30578 // either all widths or no widths..
30580 this.colWidths[0] = false; // no widths flag.
30591 mergeRight: function()
30594 // get the contents of the next cell along..
30595 var tr = this.node.closest('tr');
30596 var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
30597 if (i >= tr.childNodes.length - 1) {
30598 return; // no cells on right to merge with.
30600 var table = this.toTableArray();
30602 if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
30603 return; // nothing right?
30605 var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
30606 // right cell - must be same rowspan and on the same row.
30607 if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
30608 return; // right hand side is not same rowspan.
30613 this.node.innerHTML += ' ' + rc.cell.innerHTML;
30614 tr.removeChild(rc.cell);
30615 this.colspan += rc.colspan;
30616 this.node.setAttribute('colspan', this.colspan);
30618 var table = this.toTableArray();
30619 this.normalizeWidths(table);
30620 this.updateWidths(table);
30624 mergeBelow : function()
30626 var table = this.toTableArray();
30627 if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
30628 return; // no row below
30630 if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
30631 return; // nothing right?
30633 var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
30635 if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
30636 return; // right hand side is not same rowspan.
30638 this.node.innerHTML = this.node.innerHTML + rc.cell.innerHTML ;
30639 rc.cell.parentNode.removeChild(rc.cell);
30640 this.rowspan += rc.rowspan;
30641 this.node.setAttribute('rowspan', this.rowspan);
30646 if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
30649 var table = this.toTableArray();
30650 var cd = this.cellData;
30654 for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
30657 for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30658 if (r == cd.row && c == cd.col) {
30659 this.node.removeAttribute('rowspan');
30660 this.node.removeAttribute('colspan');
30663 var ntd = this.node.cloneNode(); // which col/row should be 0..
30664 ntd.removeAttribute('id');
30665 ntd.style.width = this.colWidths[c];
30666 ntd.innerHTML = '';
30667 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1 };
30671 this.redrawAllCells(table);
30677 redrawAllCells: function(table)
30681 var tab = this.node.closest('tr').closest('table');
30682 var ctr = tab.rows[0].parentNode;
30683 Array.from(tab.rows).forEach(function(r, ri){
30685 Array.from(r.cells).forEach(function(ce, ci){
30686 ce.parentNode.removeChild(ce);
30688 r.parentNode.removeChild(r);
30690 for(var r = 0 ; r < table.length; r++) {
30691 var re = tab.rows[r];
30693 var re = tab.ownerDocument.createElement('tr');
30694 ctr.appendChild(re);
30695 for(var c = 0 ; c < table[r].length; c++) {
30696 if (table[r][c].cell === false) {
30700 re.appendChild(table[r][c].cell);
30702 table[r][c].cell = false;
30707 updateWidths : function(table)
30709 for(var r = 0 ; r < table.length; r++) {
30711 for(var c = 0 ; c < table[r].length; c++) {
30712 if (table[r][c].cell === false) {
30716 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30717 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30718 el.width = Math.floor(this.colWidths[c]) +'%';
30719 el.updateElement(el.node);
30721 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30722 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30724 for(var i = 0; i < table[r][c].colspan; i ++) {
30725 width += Math.floor(this.colWidths[c + i]);
30727 el.width = width +'%';
30728 el.updateElement(el.node);
30730 table[r][c].cell = false; // done
30734 normalizeWidths : function(table)
30736 if (this.colWidths[0] === false) {
30737 var nw = 100.0 / this.colWidths.length;
30738 this.colWidths.forEach(function(w,i) {
30739 this.colWidths[i] = nw;
30744 var t = 0, missing = [];
30746 this.colWidths.forEach(function(w,i) {
30748 this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30749 var add = this.colWidths[i];
30758 var nc = this.colWidths.length;
30759 if (missing.length) {
30760 var mult = (nc - missing.length) / (1.0 * nc);
30762 var ew = (100 -t) / (1.0 * missing.length);
30763 this.colWidths.forEach(function(w,i) {
30765 this.colWidths[i] = w * mult;
30769 this.colWidths[i] = ew;
30771 // have to make up numbers..
30774 // now we should have all the widths..
30779 shrinkColumn : function()
30781 var table = this.toTableArray();
30782 this.normalizeWidths(table);
30783 var col = this.cellData.col;
30784 var nw = this.colWidths[col] * 0.8;
30788 var otherAdd = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
30789 this.colWidths.forEach(function(w,i) {
30791 this.colWidths[i] = nw;
30794 this.colWidths[i] += otherAdd
30796 this.updateWidths(table);
30799 growColumn : function()
30801 var table = this.toTableArray();
30802 this.normalizeWidths(table);
30803 var col = this.cellData.col;
30804 var nw = this.colWidths[col] * 1.2;
30808 var otherSub = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
30809 this.colWidths.forEach(function(w,i) {
30811 this.colWidths[i] = nw;
30814 this.colWidths[i] -= otherSub
30816 this.updateWidths(table);
30819 deleteRow : function()
30821 // delete this rows 'tr'
30822 // if any of the cells in this row have a rowspan > 1 && row!= this row..
30823 // then reduce the rowspan.
30824 var table = this.toTableArray();
30825 // this.cellData.row;
30826 for (var i =0;i< table[this.cellData.row].length ; i++) {
30827 var c = table[this.cellData.row][i];
30828 if (c.row != this.cellData.row) {
30831 c.cell.setAttribute('rowspan', c.rowspan);
30834 if (c.rowspan > 1) {
30836 c.cell.setAttribute('rowspan', c.rowspan);
30839 table.splice(this.cellData.row,1);
30840 this.redrawAllCells(table);
30843 deleteColumn : function()
30845 var table = this.toTableArray();
30847 for (var i =0;i< table.length ; i++) {
30848 var c = table[i][this.cellData.col];
30849 if (c.col != this.cellData.col) {
30850 table[i][this.cellData.col].colspan--;
30851 } else if (c.colspan > 1) {
30853 c.cell.setAttribute('colspan', c.colspan);
30855 table[i].splice(this.cellData.col,1);
30858 this.redrawAllCells(table);
30866 //<script type="text/javascript">
30869 * Based Ext JS Library 1.1.1
30870 * Copyright(c) 2006-2007, Ext JS, LLC.
30876 * @class Roo.HtmlEditorCore
30877 * @extends Roo.Component
30878 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30880 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30883 Roo.HtmlEditorCore = function(config){
30886 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30891 * @event initialize
30892 * Fires when the editor is fully initialized (including the iframe)
30893 * @param {Roo.HtmlEditorCore} this
30898 * Fires when the editor is first receives the focus. Any insertion must wait
30899 * until after this event.
30900 * @param {Roo.HtmlEditorCore} this
30904 * @event beforesync
30905 * Fires before the textarea is updated with content from the editor iframe. Return false
30906 * to cancel the sync.
30907 * @param {Roo.HtmlEditorCore} this
30908 * @param {String} html
30912 * @event beforepush
30913 * Fires before the iframe editor is updated with content from the textarea. Return false
30914 * to cancel the push.
30915 * @param {Roo.HtmlEditorCore} this
30916 * @param {String} html
30921 * Fires when the textarea is updated with content from the editor iframe.
30922 * @param {Roo.HtmlEditorCore} this
30923 * @param {String} html
30928 * Fires when the iframe editor is updated with content from the textarea.
30929 * @param {Roo.HtmlEditorCore} this
30930 * @param {String} html
30935 * @event editorevent
30936 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30937 * @param {Roo.HtmlEditorCore} this
30944 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30946 // defaults : white / black...
30947 this.applyBlacklists();
30954 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
30958 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
30964 * @cfg {String} css styling for resizing. (used on bootstrap only)
30968 * @cfg {Number} height (in pixels)
30972 * @cfg {Number} width (in pixels)
30976 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30977 * if you are doing an email editor, this probably needs disabling, it's designed
30982 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30984 enableBlocks : true,
30986 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30989 stylesheets: false,
30991 * @cfg {String} language default en - language of text (usefull for rtl languages)
30997 * @cfg {boolean} allowComments - default false - allow comments in HTML source
30998 * - by default they are stripped - if you are editing email you may need this.
31000 allowComments: false,
31004 // private properties
31005 validationEvent : false,
31007 initialized : false,
31009 sourceEditMode : false,
31010 onFocus : Roo.emptyFn,
31012 hideMode:'offsets',
31016 // blacklist + whitelisted elements..
31023 undoManager : false,
31025 * Protected method that will not generally be called directly. It
31026 * is called when the editor initializes the iframe with HTML contents. Override this method if you
31027 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
31029 getDocMarkup : function(){
31033 // inherit styels from page...??
31034 if (this.stylesheets === false) {
31036 Roo.get(document.head).select('style').each(function(node) {
31037 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
31040 Roo.get(document.head).select('link').each(function(node) {
31041 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
31044 } else if (!this.stylesheets.length) {
31046 st = '<style type="text/css">' +
31047 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
31050 for (var i in this.stylesheets) {
31051 if (typeof(this.stylesheets[i]) != 'string') {
31054 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
31059 st += '<style type="text/css">' +
31060 'IMG { cursor: pointer } ' +
31063 st += '<meta name="google" content="notranslate">';
31065 var cls = 'notranslate roo-htmleditor-body';
31067 if(this.bodyCls.length){
31068 cls += ' ' + this.bodyCls;
31071 return '<html class="notranslate" translate="no"><head>' + st +
31072 //<style type="text/css">' +
31073 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
31075 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
31079 onRender : function(ct, position)
31082 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
31083 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
31086 this.el.dom.style.border = '0 none';
31087 this.el.dom.setAttribute('tabIndex', -1);
31088 this.el.addClass('x-hidden hide');
31092 if(Roo.isIE){ // fix IE 1px bogus margin
31093 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
31097 this.frameId = Roo.id();
31101 cls: 'form-control', // bootstrap..
31103 name: this.frameId,
31104 frameBorder : 'no',
31105 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
31108 ifcfg.style = { resize : this.resize };
31111 var iframe = this.owner.wrap.createChild(ifcfg, this.el);
31114 this.iframe = iframe.dom;
31116 this.assignDocWin();
31118 this.doc.designMode = 'on';
31121 this.doc.write(this.getDocMarkup());
31125 var task = { // must defer to wait for browser to be ready
31127 //console.log("run task?" + this.doc.readyState);
31128 this.assignDocWin();
31129 if(this.doc.body || this.doc.readyState == 'complete'){
31131 this.doc.designMode="on";
31136 Roo.TaskMgr.stop(task);
31137 this.initEditor.defer(10, this);
31144 Roo.TaskMgr.start(task);
31149 onResize : function(w, h)
31151 Roo.log('resize: ' +w + ',' + h );
31152 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
31156 if(typeof w == 'number'){
31158 this.iframe.style.width = w + 'px';
31160 if(typeof h == 'number'){
31162 this.iframe.style.height = h + 'px';
31164 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
31171 * Toggles the editor between standard and source edit mode.
31172 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
31174 toggleSourceEdit : function(sourceEditMode){
31176 this.sourceEditMode = sourceEditMode === true;
31178 if(this.sourceEditMode){
31180 Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']); //FIXME - what's the BS styles for these
31183 Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
31184 //this.iframe.className = '';
31187 //this.setSize(this.owner.wrap.getSize());
31188 //this.fireEvent('editmodechange', this, this.sourceEditMode);
31195 * Protected method that will not generally be called directly. If you need/want
31196 * custom HTML cleanup, this is the method you should override.
31197 * @param {String} html The HTML to be cleaned
31198 * return {String} The cleaned HTML
31200 cleanHtml : function(html)
31202 html = String(html);
31203 if(html.length > 5){
31204 if(Roo.isSafari){ // strip safari nonsense
31205 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
31208 if(html == ' '){
31215 * HTML Editor -> Textarea
31216 * Protected method that will not generally be called directly. Syncs the contents
31217 * of the editor iframe with the textarea.
31219 syncValue : function()
31221 //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
31222 if(this.initialized){
31224 if (this.undoManager) {
31225 this.undoManager.addEvent();
31229 var bd = (this.doc.body || this.doc.documentElement);
31232 var sel = this.win.getSelection();
31234 var div = document.createElement('div');
31235 div.innerHTML = bd.innerHTML;
31236 var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
31237 if (gtx.length > 0) {
31238 var rm = gtx.item(0).parentNode;
31239 rm.parentNode.removeChild(rm);
31243 if (this.enableBlocks) {
31244 new Roo.htmleditor.FilterBlock({ node : div });
31247 var html = div.innerHTML;
31250 if (this.autoClean) {
31252 new Roo.htmleditor.FilterAttributes({
31274 attrib_clean : ['href', 'src' ]
31277 var tidy = new Roo.htmleditor.TidySerializer({
31280 html = tidy.serialize(div);
31286 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
31287 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
31289 html = '<div style="'+m[0]+'">' + html + '</div>';
31292 html = this.cleanHtml(html);
31293 // fix up the special chars.. normaly like back quotes in word...
31294 // however we do not want to do this with chinese..
31295 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
31297 var cc = match.charCodeAt();
31299 // Get the character value, handling surrogate pairs
31300 if (match.length == 2) {
31301 // It's a surrogate pair, calculate the Unicode code point
31302 var high = match.charCodeAt(0) - 0xD800;
31303 var low = match.charCodeAt(1) - 0xDC00;
31304 cc = (high * 0x400) + low + 0x10000;
31306 (cc >= 0x4E00 && cc < 0xA000 ) ||
31307 (cc >= 0x3400 && cc < 0x4E00 ) ||
31308 (cc >= 0xf900 && cc < 0xfb00 )
31313 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
31314 return "&#" + cc + ";";
31321 if(this.owner.fireEvent('beforesync', this, html) !== false){
31322 this.el.dom.value = html;
31323 this.owner.fireEvent('sync', this, html);
31329 * TEXTAREA -> EDITABLE
31330 * Protected method that will not generally be called directly. Pushes the value of the textarea
31331 * into the iframe editor.
31333 pushValue : function()
31335 //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
31336 if(this.initialized){
31337 var v = this.el.dom.value.trim();
31340 if(this.owner.fireEvent('beforepush', this, v) !== false){
31341 var d = (this.doc.body || this.doc.documentElement);
31344 this.el.dom.value = d.innerHTML;
31345 this.owner.fireEvent('push', this, v);
31347 if (this.autoClean) {
31348 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
31349 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
31351 if (this.enableBlocks) {
31352 Roo.htmleditor.Block.initAll(this.doc.body);
31355 this.updateLanguage();
31357 var lc = this.doc.body.lastChild;
31358 if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
31359 // add an extra line at the end.
31360 this.doc.body.appendChild(this.doc.createElement('br'));
31368 deferFocus : function(){
31369 this.focus.defer(10, this);
31373 focus : function(){
31374 if(this.win && !this.sourceEditMode){
31381 assignDocWin: function()
31383 var iframe = this.iframe;
31386 this.doc = iframe.contentWindow.document;
31387 this.win = iframe.contentWindow;
31389 // if (!Roo.get(this.frameId)) {
31392 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31393 // this.win = Roo.get(this.frameId).dom.contentWindow;
31395 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
31399 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31400 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
31405 initEditor : function(){
31406 //console.log("INIT EDITOR");
31407 this.assignDocWin();
31411 this.doc.designMode="on";
31413 this.doc.write(this.getDocMarkup());
31416 var dbody = (this.doc.body || this.doc.documentElement);
31417 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
31418 // this copies styles from the containing element into thsi one..
31419 // not sure why we need all of this..
31420 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
31422 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
31423 //ss['background-attachment'] = 'fixed'; // w3c
31424 dbody.bgProperties = 'fixed'; // ie
31425 dbody.setAttribute("translate", "no");
31427 //Roo.DomHelper.applyStyles(dbody, ss);
31428 Roo.EventManager.on(this.doc, {
31430 'mouseup': this.onEditorEvent,
31431 'dblclick': this.onEditorEvent,
31432 'click': this.onEditorEvent,
31433 'keyup': this.onEditorEvent,
31438 Roo.EventManager.on(this.doc, {
31439 'paste': this.onPasteEvent,
31443 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
31446 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
31447 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
31449 this.initialized = true;
31452 // initialize special key events - enter
31453 new Roo.htmleditor.KeyEnter({core : this});
31457 this.owner.fireEvent('initialize', this);
31460 // this is to prevent a href clicks resulting in a redirect?
31462 onPasteEvent : function(e,v)
31464 // I think we better assume paste is going to be a dirty load of rubish from word..
31466 // even pasting into a 'email version' of this widget will have to clean up that mess.
31467 var cd = (e.browserEvent.clipboardData || window.clipboardData);
31469 // check what type of paste - if it's an image, then handle it differently.
31470 if (cd.files && cd.files.length > 0 && cd.types.indexOf('text/html') < 0) {
31472 var urlAPI = (window.createObjectURL && window) ||
31473 (window.URL && URL.revokeObjectURL && URL) ||
31474 (window.webkitURL && webkitURL);
31476 var r = new FileReader();
31478 r.addEventListener('load',function()
31481 var d = (new DOMParser().parseFromString('<img src="' + r.result+ '">', 'text/html')).body;
31482 // is insert asycn?
31483 if (t.enableBlocks) {
31485 Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31486 if (img.closest('figure')) { // assume!! that it's aready
31489 var fig = new Roo.htmleditor.BlockFigure({
31490 image_src : img.src
31492 fig.updateElement(img); // replace it..
31496 t.insertAtCursor(d.innerHTML.replace(/ /g,' '));
31497 t.owner.fireEvent('paste', this);
31499 r.readAsDataURL(cd.files[0]);
31501 e.preventDefault();
31505 if (cd.types.indexOf('text/html') < 0 ) {
31509 var html = cd.getData('text/html'); // clipboard event
31510 if (cd.types.indexOf('text/rtf') > -1) {
31511 var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
31512 images = parser.doc ? parser.doc.getElementsByType('pict') : [];
31514 // Roo.log(images);
31517 images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
31518 .map(function(g) { return g.toDataURL(); })
31519 .filter(function(g) { return g != 'about:blank'; });
31522 html = this.cleanWordChars(html);
31524 var d = (new DOMParser().parseFromString(html, 'text/html')).body;
31527 var sn = this.getParentElement();
31528 // check if d contains a table, and prevent nesting??
31529 //Roo.log(d.getElementsByTagName('table'));
31531 //Roo.log(sn.closest('table'));
31532 if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
31533 e.preventDefault();
31534 this.insertAtCursor("You can not nest tables");
31535 //Roo.log("prevent?"); // fixme -
31541 if (images.length > 0) {
31542 // replace all v:imagedata - with img.
31543 var ar = Array.from(d.getElementsByTagName('v:imagedata'));
31544 Roo.each(ar, function(node) {
31545 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
31546 node.parentNode.removeChild(node);
31550 Roo.each(d.getElementsByTagName('img'), function(img, i) {
31551 img.setAttribute('src', images[i]);
31554 if (this.autoClean) {
31555 new Roo.htmleditor.FilterWord({ node : d });
31557 new Roo.htmleditor.FilterStyleToTag({ node : d });
31558 new Roo.htmleditor.FilterAttributes({
31560 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
31561 attrib_clean : ['href', 'src' ]
31563 new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
31564 // should be fonts..
31565 new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
31566 new Roo.htmleditor.FilterParagraph({ node : d });
31567 new Roo.htmleditor.FilterSpan({ node : d });
31568 new Roo.htmleditor.FilterLongBr({ node : d });
31569 new Roo.htmleditor.FilterComment({ node : d });
31573 if (this.enableBlocks) {
31575 Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31576 if (img.closest('figure')) { // assume!! that it's aready
31579 var fig = new Roo.htmleditor.BlockFigure({
31580 image_src : img.src
31582 fig.updateElement(img); // replace it..
31588 this.insertAtCursor(d.innerHTML.replace(/ /g,' '));
31589 if (this.enableBlocks) {
31590 Roo.htmleditor.Block.initAll(this.doc.body);
31594 e.preventDefault();
31595 this.owner.fireEvent('paste', this);
31597 // default behaveiour should be our local cleanup paste? (optional?)
31598 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
31599 //this.owner.fireEvent('paste', e, v);
31602 onDestroy : function(){
31608 //for (var i =0; i < this.toolbars.length;i++) {
31609 // // fixme - ask toolbars for heights?
31610 // this.toolbars[i].onDestroy();
31613 //this.wrap.dom.innerHTML = '';
31614 //this.wrap.remove();
31619 onFirstFocus : function(){
31621 this.assignDocWin();
31622 this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
31624 this.activated = true;
31627 if(Roo.isGecko){ // prevent silly gecko errors
31629 var s = this.win.getSelection();
31630 if(!s.focusNode || s.focusNode.nodeType != 3){
31631 var r = s.getRangeAt(0);
31632 r.selectNodeContents((this.doc.body || this.doc.documentElement));
31637 this.execCmd('useCSS', true);
31638 this.execCmd('styleWithCSS', false);
31641 this.owner.fireEvent('activate', this);
31645 adjustFont: function(btn){
31646 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
31647 //if(Roo.isSafari){ // safari
31650 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
31651 if(Roo.isSafari){ // safari
31652 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
31653 v = (v < 10) ? 10 : v;
31654 v = (v > 48) ? 48 : v;
31655 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
31660 v = Math.max(1, v+adjust);
31662 this.execCmd('FontSize', v );
31665 onEditorEvent : function(e)
31669 if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
31670 return; // we do not handle this.. (undo manager does..)
31672 // clicking a 'block'?
31674 // in theory this detects if the last element is not a br, then we try and do that.
31675 // its so clicking in space at bottom triggers adding a br and moving the cursor.
31677 e.target.nodeName == 'BODY' &&
31678 e.type == "mouseup" &&
31679 this.doc.body.lastChild
31681 var lc = this.doc.body.lastChild;
31682 // gtx-trans is google translate plugin adding crap.
31683 while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31684 lc = lc.previousSibling;
31686 if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31687 // if last element is <BR> - then dont do anything.
31689 var ns = this.doc.createElement('br');
31690 this.doc.body.appendChild(ns);
31691 range = this.doc.createRange();
31692 range.setStartAfter(ns);
31693 range.collapse(true);
31694 var sel = this.win.getSelection();
31695 sel.removeAllRanges();
31696 sel.addRange(range);
31702 this.fireEditorEvent(e);
31703 // this.updateToolbar();
31704 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31707 fireEditorEvent: function(e)
31709 this.owner.fireEvent('editorevent', this, e);
31712 insertTag : function(tg)
31714 // could be a bit smarter... -> wrap the current selected tRoo..
31715 if (tg.toLowerCase() == 'span' ||
31716 tg.toLowerCase() == 'code' ||
31717 tg.toLowerCase() == 'sup' ||
31718 tg.toLowerCase() == 'sub'
31721 range = this.createRange(this.getSelection());
31722 var wrappingNode = this.doc.createElement(tg.toLowerCase());
31723 wrappingNode.appendChild(range.extractContents());
31724 range.insertNode(wrappingNode);
31731 this.execCmd("formatblock", tg);
31732 this.undoManager.addEvent();
31735 insertText : function(txt)
31739 var range = this.createRange();
31740 range.deleteContents();
31741 //alert(Sender.getAttribute('label'));
31743 range.insertNode(this.doc.createTextNode(txt));
31744 this.undoManager.addEvent();
31750 * Executes a Midas editor command on the editor document and performs necessary focus and
31751 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31752 * @param {String} cmd The Midas command
31753 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31755 relayCmd : function(cmd, value)
31759 case 'justifyleft':
31760 case 'justifyright':
31761 case 'justifycenter':
31762 // if we are in a cell, then we will adjust the
31763 var n = this.getParentElement();
31764 var td = n.closest('td');
31766 var bl = Roo.htmleditor.Block.factory(td);
31767 bl.textAlign = cmd.replace('justify','');
31768 bl.updateElement();
31769 this.owner.fireEvent('editorevent', this);
31772 this.execCmd('styleWithCSS', true); //
31777 // if there is no selection, then we insert, and set the curson inside it..
31778 this.execCmd('styleWithCSS', false);
31788 this.execCmd(cmd, value);
31789 this.owner.fireEvent('editorevent', this);
31790 //this.updateToolbar();
31791 this.owner.deferFocus();
31795 * Executes a Midas editor command directly on the editor document.
31796 * For visual commands, you should use {@link #relayCmd} instead.
31797 * <b>This should only be called after the editor is initialized.</b>
31798 * @param {String} cmd The Midas command
31799 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31801 execCmd : function(cmd, value){
31802 this.doc.execCommand(cmd, false, value === undefined ? null : value);
31809 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31811 * @param {String} text | dom node..
31813 insertAtCursor : function(text)
31816 if(!this.activated){
31820 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31824 // from jquery ui (MIT licenced)
31826 var win = this.win;
31828 if (win.getSelection && win.getSelection().getRangeAt) {
31830 // delete the existing?
31832 this.createRange(this.getSelection()).deleteContents();
31833 range = win.getSelection().getRangeAt(0);
31834 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31835 range.insertNode(node);
31836 range = range.cloneRange();
31837 range.collapse(false);
31839 win.getSelection().removeAllRanges();
31840 win.getSelection().addRange(range);
31844 } else if (win.document.selection && win.document.selection.createRange) {
31845 // no firefox support
31846 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31847 win.document.selection.createRange().pasteHTML(txt);
31850 // no firefox support
31851 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31852 this.execCmd('InsertHTML', txt);
31860 mozKeyPress : function(e){
31862 var c = e.getCharCode(), cmd;
31865 c = String.fromCharCode(c).toLowerCase();
31879 // this.cleanUpPaste.defer(100, this);
31885 this.relayCmd(cmd);
31886 //this.win.focus();
31887 //this.execCmd(cmd);
31888 //this.deferFocus();
31889 e.preventDefault();
31897 fixKeys : function(){ // load time branching for fastest keydown performance
31901 return function(e){
31902 var k = e.getKey(), r;
31905 r = this.doc.selection.createRange();
31908 r.pasteHTML('    ');
31913 /// this is handled by Roo.htmleditor.KeyEnter
31916 r = this.doc.selection.createRange();
31918 var target = r.parentElement();
31919 if(!target || target.tagName.toLowerCase() != 'li'){
31921 r.pasteHTML('<br/>');
31928 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31929 // this.cleanUpPaste.defer(100, this);
31935 }else if(Roo.isOpera){
31936 return function(e){
31937 var k = e.getKey();
31941 this.execCmd('InsertHTML','    ');
31945 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31946 // this.cleanUpPaste.defer(100, this);
31951 }else if(Roo.isSafari){
31952 return function(e){
31953 var k = e.getKey();
31957 this.execCmd('InsertText','\t');
31961 this.mozKeyPress(e);
31963 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31964 // this.cleanUpPaste.defer(100, this);
31972 getAllAncestors: function()
31974 var p = this.getSelectedNode();
31977 a.push(p); // push blank onto stack..
31978 p = this.getParentElement();
31982 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31986 a.push(this.doc.body);
31990 lastSelNode : false,
31993 getSelection : function()
31995 this.assignDocWin();
31996 return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31999 * Select a dom node
32000 * @param {DomElement} node the node to select
32002 selectNode : function(node, collapse)
32004 var nodeRange = node.ownerDocument.createRange();
32006 nodeRange.selectNode(node);
32008 nodeRange.selectNodeContents(node);
32010 if (collapse === true) {
32011 nodeRange.collapse(true);
32014 var s = this.win.getSelection();
32015 s.removeAllRanges();
32016 s.addRange(nodeRange);
32019 getSelectedNode: function()
32021 // this may only work on Gecko!!!
32023 // should we cache this!!!!
32027 var range = this.createRange(this.getSelection()).cloneRange();
32030 var parent = range.parentElement();
32032 var testRange = range.duplicate();
32033 testRange.moveToElementText(parent);
32034 if (testRange.inRange(range)) {
32037 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
32040 parent = parent.parentElement;
32045 // is ancestor a text element.
32046 var ac = range.commonAncestorContainer;
32047 if (ac.nodeType == 3) {
32048 ac = ac.parentNode;
32051 var ar = ac.childNodes;
32054 var other_nodes = [];
32055 var has_other_nodes = false;
32056 for (var i=0;i<ar.length;i++) {
32057 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
32060 // fullly contained node.
32062 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
32067 // probably selected..
32068 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
32069 other_nodes.push(ar[i]);
32073 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
32078 has_other_nodes = true;
32080 if (!nodes.length && other_nodes.length) {
32081 nodes= other_nodes;
32083 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
32091 createRange: function(sel)
32093 // this has strange effects when using with
32094 // top toolbar - not sure if it's a great idea.
32095 //this.editor.contentWindow.focus();
32096 if (typeof sel != "undefined") {
32098 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
32100 return this.doc.createRange();
32103 return this.doc.createRange();
32106 getParentElement: function()
32109 this.assignDocWin();
32110 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
32112 var range = this.createRange(sel);
32115 var p = range.commonAncestorContainer;
32116 while (p.nodeType == 3) { // text node
32127 * Range intersection.. the hard stuff...
32131 * [ -- selected range --- ]
32135 * if end is before start or hits it. fail.
32136 * if start is after end or hits it fail.
32138 * if either hits (but other is outside. - then it's not
32144 // @see http://www.thismuchiknow.co.uk/?p=64.
32145 rangeIntersectsNode : function(range, node)
32147 var nodeRange = node.ownerDocument.createRange();
32149 nodeRange.selectNode(node);
32151 nodeRange.selectNodeContents(node);
32154 var rangeStartRange = range.cloneRange();
32155 rangeStartRange.collapse(true);
32157 var rangeEndRange = range.cloneRange();
32158 rangeEndRange.collapse(false);
32160 var nodeStartRange = nodeRange.cloneRange();
32161 nodeStartRange.collapse(true);
32163 var nodeEndRange = nodeRange.cloneRange();
32164 nodeEndRange.collapse(false);
32166 return rangeStartRange.compareBoundaryPoints(
32167 Range.START_TO_START, nodeEndRange) == -1 &&
32168 rangeEndRange.compareBoundaryPoints(
32169 Range.START_TO_START, nodeStartRange) == 1;
32173 rangeCompareNode : function(range, node)
32175 var nodeRange = node.ownerDocument.createRange();
32177 nodeRange.selectNode(node);
32179 nodeRange.selectNodeContents(node);
32183 range.collapse(true);
32185 nodeRange.collapse(true);
32187 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
32188 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
32190 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
32192 var nodeIsBefore = ss == 1;
32193 var nodeIsAfter = ee == -1;
32195 if (nodeIsBefore && nodeIsAfter) {
32198 if (!nodeIsBefore && nodeIsAfter) {
32199 return 1; //right trailed.
32202 if (nodeIsBefore && !nodeIsAfter) {
32203 return 2; // left trailed.
32209 cleanWordChars : function(input) {// change the chars to hex code
32212 [ 8211, "–" ],
32213 [ 8212, "—" ],
32221 var output = input;
32222 Roo.each(swapCodes, function(sw) {
32223 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
32225 output = output.replace(swapper, sw[1]);
32235 cleanUpChild : function (node)
32238 new Roo.htmleditor.FilterComment({node : node});
32239 new Roo.htmleditor.FilterAttributes({
32241 attrib_black : this.ablack,
32242 attrib_clean : this.aclean,
32243 style_white : this.cwhite,
32244 style_black : this.cblack
32246 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
32247 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
32253 * Clean up MS wordisms...
32254 * @deprecated - use filter directly
32256 cleanWord : function(node)
32258 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
32259 new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
32266 * @deprecated - use filters
32268 cleanTableWidths : function(node)
32270 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
32277 applyBlacklists : function()
32279 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
32280 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
32282 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
32283 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
32284 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
32288 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
32289 if (b.indexOf(tag) > -1) {
32292 this.white.push(tag);
32296 Roo.each(w, function(tag) {
32297 if (b.indexOf(tag) > -1) {
32300 if (this.white.indexOf(tag) > -1) {
32303 this.white.push(tag);
32308 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
32309 if (w.indexOf(tag) > -1) {
32312 this.black.push(tag);
32316 Roo.each(b, function(tag) {
32317 if (w.indexOf(tag) > -1) {
32320 if (this.black.indexOf(tag) > -1) {
32323 this.black.push(tag);
32328 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
32329 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
32333 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
32334 if (b.indexOf(tag) > -1) {
32337 this.cwhite.push(tag);
32341 Roo.each(w, function(tag) {
32342 if (b.indexOf(tag) > -1) {
32345 if (this.cwhite.indexOf(tag) > -1) {
32348 this.cwhite.push(tag);
32353 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
32354 if (w.indexOf(tag) > -1) {
32357 this.cblack.push(tag);
32361 Roo.each(b, function(tag) {
32362 if (w.indexOf(tag) > -1) {
32365 if (this.cblack.indexOf(tag) > -1) {
32368 this.cblack.push(tag);
32373 setStylesheets : function(stylesheets)
32375 if(typeof(stylesheets) == 'string'){
32376 Roo.get(this.iframe.contentDocument.head).createChild({
32378 rel : 'stylesheet',
32387 Roo.each(stylesheets, function(s) {
32392 Roo.get(_this.iframe.contentDocument.head).createChild({
32394 rel : 'stylesheet',
32404 updateLanguage : function()
32406 if (!this.iframe || !this.iframe.contentDocument) {
32409 Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
32413 removeStylesheets : function()
32417 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
32422 setStyle : function(style)
32424 Roo.get(this.iframe.contentDocument.head).createChild({
32433 // hide stuff that is not compatible
32447 * @event specialkey
32451 * @cfg {String} fieldClass @hide
32454 * @cfg {String} focusClass @hide
32457 * @cfg {String} autoCreate @hide
32460 * @cfg {String} inputType @hide
32463 * @cfg {String} invalidClass @hide
32466 * @cfg {String} invalidText @hide
32469 * @cfg {String} msgFx @hide
32472 * @cfg {String} validateOnBlur @hide
32476 Roo.HtmlEditorCore.white = [
32477 'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
32479 'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD', 'DIR', 'DIV',
32480 'DL', 'DT', 'H1', 'H2', 'H3', 'H4',
32481 'H5', 'H6', 'HR', 'ISINDEX', 'LISTING', 'MARQUEE',
32482 'MENU', 'MULTICOL', 'OL', 'P', 'PLAINTEXT', 'PRE',
32483 'TABLE', 'UL', 'XMP',
32485 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
32488 'DIR', 'MENU', 'OL', 'UL', 'DL',
32494 Roo.HtmlEditorCore.black = [
32495 // 'embed', 'object', // enable - backend responsiblity to clean thiese
32497 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
32498 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
32499 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
32500 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
32501 //'FONT' // CLEAN LATER..
32502 'COLGROUP', 'COL' // messy tables.
32506 Roo.HtmlEditorCore.clean = [ // ?? needed???
32507 'SCRIPT', 'STYLE', 'TITLE', 'XML'
32509 Roo.HtmlEditorCore.tag_remove = [
32514 Roo.HtmlEditorCore.ablack = [
32518 Roo.HtmlEditorCore.aclean = [
32519 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
32523 Roo.HtmlEditorCore.pwhite= [
32524 'http', 'https', 'mailto'
32527 // white listed style attributes.
32528 Roo.HtmlEditorCore.cwhite= [
32529 // 'text-align', /// default is to allow most things..
32535 // black listed style attributes.
32536 Roo.HtmlEditorCore.cblack= [
32537 // 'font-size' -- this can be set by the project
32551 * @class Roo.bootstrap.form.HtmlEditor
32552 * @extends Roo.bootstrap.form.TextArea
32553 * Bootstrap HtmlEditor class
32556 * Create a new HtmlEditor
32557 * @param {Object} config The config object
32560 Roo.bootstrap.form.HtmlEditor = function(config){
32564 * @event initialize
32565 * Fires when the editor is fully initialized (including the iframe)
32566 * @param {Roo.bootstrap.form.HtmlEditor} this
32571 * Fires when the editor is first receives the focus. Any insertion must wait
32572 * until after this event.
32573 * @param {Roo.bootstrap.form.HtmlEditor} this
32577 * @event beforesync
32578 * Fires before the textarea is updated with content from the editor iframe. Return false
32579 * to cancel the sync.
32580 * @param {Roo.bootstrap.form.HtmlEditor} this
32581 * @param {String} html
32585 * @event beforepush
32586 * Fires before the iframe editor is updated with content from the textarea. Return false
32587 * to cancel the push.
32588 * @param {Roo.bootstrap.form.HtmlEditor} this
32589 * @param {String} html
32594 * Fires when the textarea is updated with content from the editor iframe.
32595 * @param {Roo.bootstrap.form.HtmlEditor} this
32596 * @param {String} html
32601 * Fires when the iframe editor is updated with content from the textarea.
32602 * @param {Roo.bootstrap.form.HtmlEditor} this
32603 * @param {String} html
32607 * @event editmodechange
32608 * Fires when the editor switches edit modes
32609 * @param {Roo.bootstrap.form.HtmlEditor} this
32610 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
32612 editmodechange: true,
32614 * @event editorevent
32615 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
32616 * @param {Roo.bootstrap.form.HtmlEditor} this
32620 * @event firstfocus
32621 * Fires when on first focus - needed by toolbars..
32622 * @param {Roo.bootstrap.form.HtmlEditor} this
32627 * Auto save the htmlEditor value as a file into Events
32628 * @param {Roo.bootstrap.form.HtmlEditor} this
32632 * @event savedpreview
32633 * preview the saved version of htmlEditor
32634 * @param {Roo.bootstrap.form.HtmlEditor} this
32636 savedpreview: true,
32638 * @event stylesheetsclick
32639 * Fires when press the Sytlesheets button
32640 * @param {Roo.HtmlEditorCore} this
32642 stylesheetsclick: true,
32645 * Fires when press user pastes into the editor
32646 * @param {Roo.HtmlEditorCore} this
32651 * Fires when on any editor when an image is added (excluding paste)
32652 * @param {Roo.bootstrap.form.HtmlEditor} this
32656 * @event imageupdated
32657 * Fires when on any editor when an image is changed (excluding paste)
32658 * @param {Roo.bootstrap.form.HtmlEditor} this
32659 * @param {HTMLElement} img could also be a figure if blocks are enabled
32661 imageupdate: true ,
32663 * @event imagedelete
32664 * Fires when on any editor when an image is deleted
32665 * @param {Roo.bootstrap.form.HtmlEditor} this
32666 * @param {HTMLElement} img could also be a figure if blocks are enabled
32667 * @param {HTMLElement} oldSrc source of image being replaced
32671 Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
32672 if (!this.toolbars) {
32673 this.toolbars = [];
32676 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
32681 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, {
32685 * @cfg {Array|boolean} toolbars Array of toolbars, or names of toolbars. - true for standard, and false for none.
32690 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
32695 * @cfg {String} resize (none|both|horizontal|vertical) - css resize of element
32699 * @cfg {Number} height (in pixels)
32703 * @cfg {Number} width (in pixels)
32708 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
32711 stylesheets: false,
32716 // private properties
32717 validationEvent : false,
32719 initialized : false,
32722 onFocus : Roo.emptyFn,
32724 hideMode:'offsets',
32726 tbContainer : false,
32730 linkDialogCls : '',
32732 toolbarContainer :function() {
32733 return this.wrap.select('.x-html-editor-tb',true).first();
32737 * Protected method that will not generally be called directly. It
32738 * is called when the editor creates its toolbar. Override this method if you need to
32739 * add custom toolbar buttons.
32740 * @param {HtmlEditor} editor
32742 createToolbar : function()
32744 //Roo.log('renewing');
32745 //Roo.log("create toolbars");
32746 if (this.toolbars === false) {
32749 if (this.toolbars === true) {
32750 this.toolbars = [ 'Standard' ];
32753 var ar = Array.from(this.toolbars);
32754 this.toolbars = [];
32755 ar.forEach(function(t,i) {
32756 if (typeof(t) == 'string') {
32761 if (typeof(t) == 'object' && typeof(t.xtype) == 'string') {
32763 t.xns = t.xns || Roo.bootstrap.form.HtmlEditorToolbar;
32764 t = Roo.factory(t);
32766 this.toolbars[i] = t;
32767 this.toolbars[i].render(this.toolbarContainer());
32775 onRender : function(ct, position)
32777 // Roo.log("Call onRender: " + this.xtype);
32779 Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32781 this.wrap = this.inputEl().wrap({
32782 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32785 this.editorcore.onRender(ct, position);
32788 this.createToolbar(this);
32796 onResize : function(w, h)
32798 Roo.log('resize: ' +w + ',' + h );
32799 Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32803 if(this.inputEl() ){
32804 if(typeof w == 'number'){
32805 var aw = w - this.wrap.getFrameWidth('lr');
32806 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32809 if(typeof h == 'number'){
32810 var tbh = -11; // fixme it needs to tool bar size!
32811 for (var i =0; i < this.toolbars.length;i++) {
32812 // fixme - ask toolbars for heights?
32813 tbh += this.toolbars[i].el.getHeight();
32814 //if (this.toolbars[i].footer) {
32815 // tbh += this.toolbars[i].footer.el.getHeight();
32823 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32824 ah -= 5; // knock a few pixes off for look..
32825 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32829 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32830 this.editorcore.onResize(ew,eh);
32835 * Toggles the editor between standard and source edit mode.
32836 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32838 toggleSourceEdit : function(sourceEditMode)
32840 this.editorcore.toggleSourceEdit(sourceEditMode);
32842 if(this.editorcore.sourceEditMode){
32843 Roo.log('editor - showing textarea');
32846 // Roo.log(this.syncValue());
32848 this.inputEl().removeClass(['hide', 'x-hidden']);
32849 this.inputEl().dom.removeAttribute('tabIndex');
32850 this.inputEl().focus();
32852 Roo.log('editor - hiding textarea');
32854 // Roo.log(this.pushValue());
32857 this.inputEl().addClass(['hide', 'x-hidden']);
32858 this.inputEl().dom.setAttribute('tabIndex', -1);
32859 //this.deferFocus();
32862 //if(this.resizable){
32863 // this.setSize(this.wrap.getSize());
32866 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32869 // private (for BoxComponent)
32870 adjustSize : Roo.BoxComponent.prototype.adjustSize,
32872 // private (for BoxComponent)
32873 getResizeEl : function(){
32877 // private (for BoxComponent)
32878 getPositionEl : function(){
32883 initEvents : function(){
32884 this.originalValue = this.getValue();
32888 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32891 // markInvalid : Roo.emptyFn,
32893 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32896 // clearInvalid : Roo.emptyFn,
32898 setValue : function(v){
32899 Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32900 this.editorcore.pushValue();
32905 deferFocus : function(){
32906 this.focus.defer(10, this);
32910 focus : function(){
32911 this.editorcore.focus();
32917 onDestroy : function(){
32923 for (var i =0; i < this.toolbars.length;i++) {
32924 // fixme - ask toolbars for heights?
32925 this.toolbars[i].onDestroy();
32928 this.wrap.dom.innerHTML = '';
32929 this.wrap.remove();
32934 onFirstFocus : function(){
32935 //Roo.log("onFirstFocus");
32936 this.editorcore.onFirstFocus();
32937 for (var i =0; i < this.toolbars.length;i++) {
32938 this.toolbars[i].onFirstFocus();
32944 syncValue : function()
32946 this.editorcore.syncValue();
32949 pushValue : function()
32951 this.editorcore.pushValue();
32955 // hide stuff that is not compatible
32969 * @event specialkey
32973 * @cfg {String} fieldClass @hide
32976 * @cfg {String} focusClass @hide
32979 * @cfg {String} autoCreate @hide
32982 * @cfg {String} inputType @hide
32986 * @cfg {String} invalidText @hide
32989 * @cfg {String} msgFx @hide
32992 * @cfg {String} validateOnBlur @hide
33002 * @class Roo.bootstrap.form.HtmlEditorToolbar.Standard
33003 * @parent Roo.bootstrap.form.HtmlEditor
33004 * @extends Roo.bootstrap.nav.Simplebar
33010 new Roo.bootstrap.form.HtmlEditor({
33013 new Roo.bootstrap.form.HtmlEditorToolbar.Standard({
33014 disable : { fonts: 1 , format: 1, ..., ... , ...],
33020 * @cfg {Object} disable List of elements to disable..
33021 * @cfg {Array} btns List of additional buttons.
33025 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
33028 Roo.bootstrap.form.HtmlEditorToolbar.Standard = function(config)
33031 Roo.apply(this, config);
33033 // default disabled, based on 'good practice'..
33034 this.disable = this.disable || {};
33035 Roo.applyIf(this.disable, {
33038 specialElements : true
33040 Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.constructor.call(this, config);
33042 this.editor = config.editor;
33043 this.editorcore = config.editor.editorcore;
33045 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.btnid; });
33047 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
33048 // dont call parent... till later.
33050 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbar.Standard, Roo.bootstrap.nav.Simplebar, {
33055 editorcore : false,
33060 "h1","h2","h3","h4","h5","h6",
33062 "abbr", "acronym", "address", "cite", "samp", "var",
33069 onRender : function(ct, position)
33071 // Roo.log("Call onRender: " + this.xtype);
33073 Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.onRender.call(this, ct, position);
33075 this.el.dom.style.marginBottom = '0';
33077 var editorcore = this.editorcore;
33078 var editor= this.editor;
33081 var btn = function(id, cmd , toggle, handler, html){
33083 var event = toggle ? 'toggle' : 'click';
33088 xns: Roo.bootstrap,
33092 cls : 'roo-html-editor-btn-' + id,
33093 cmd : cmd, // why id || cmd
33094 enableToggle: toggle !== false,
33096 pressed : toggle ? false : null,
33099 a.listeners[toggle ? 'toggle' : 'click'] = function() {
33100 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
33106 // var cb_box = function...
33111 xns: Roo.bootstrap,
33113 cls : 'roo-html-editor-font-chooser',
33117 xns: Roo.bootstrap,
33121 Roo.each(this.formats, function(f) {
33122 style.menu.items.push({
33124 xns: Roo.bootstrap,
33125 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
33130 editorcore.insertTag(this.tagname);
33137 children.push(style);
33139 btn('bold', 'bold',true);
33140 btn('italic', 'italic',true);
33141 btn('underline', 'underline',true);
33142 btn('align-left', 'justifyleft',true);
33143 btn('align-center', 'justifycenter',true);
33144 btn('align-right' , 'justifyright',true);
33145 btn('link', false, true, this.onLinkClick);
33148 btn('image', false, true, this.onImageClick);
33149 btn('list','insertunorderedlist',true);
33150 btn('list-ol','insertorderedlist',true);
33152 btn('pencil', false,true, function(btn){
33154 this.toggleSourceEdit(btn.pressed);
33157 if (this.editor.btns.length > 0) {
33158 for (var i = 0; i<this.editor.btns.length; i++) {
33159 children.push(this.editor.btns[i]);
33165 this.xtype = 'NavSimplebar'; // why?
33167 for(var i=0;i< children.length;i++) {
33169 this.buttons.add(this.addxtypeChild(children[i]));
33172 this.buildToolbarDelete();
33174 editor.on('editorevent', this.updateToolbar, this);
33177 buildToolbarDelete : function()
33180 /* this.addxtypeChild({
33182 xns : Roo.bootstrap,
33183 cls : 'roo-htmleditor-fill'
33186 this.deleteBtn = this.addxtypeChild({
33189 xns: Roo.bootstrap,
33192 click : this.onDelete.createDelegate(this)
33195 this.deleteBtn.hide();
33199 onImageClick : function()
33202 this.input.un('change', this.onFileSelected, this);
33204 this.input = Roo.get(document.body).createChild({
33207 style : 'display:none',
33208 multiple: 'multiple'
33210 this.input.on('change', this.onFileSelected, this);
33211 this.input.dom.click();
33214 onFileSelected : function(e)
33216 e.preventDefault();
33218 if(typeof(this.input.dom.files) == 'undefined' || !this.input.dom.files.length){
33223 this.addFiles(Array.prototype.slice.call(this.input.dom.files), false);
33226 addFiles : function(far, fire_add) {
33229 var editor = this.editorcore;
33233 this.editor.syncValue();
33234 editor.owner.fireEvent('editorevent', editor.owner, false);
33235 editor.owner.fireEvent('imageadd', editor.owner, false);
33242 if (!f.type.match(/^image/)) {
33243 this.addFiles(far, fire_add);
33247 var sn = this.selectedNode;
33249 var bl = sn && this.editorcore.enableBlocks ? Roo.htmleditor.Block.factory(sn) : false;
33252 var reader = new FileReader();
33253 reader.addEventListener('load', (function() {
33255 var oldSrc = bl.image_src;
33256 bl.image_src = reader.result;
33257 //bl.caption = f.name;
33258 bl.updateElement(sn);
33259 this.editor.syncValue();
33260 editor.owner.fireEvent('editorevent', editor.owner, false);
33261 editor.owner.fireEvent('imageupdate', editor.owner, sn, oldSrc);
33262 // we only do the first file!! and replace.
33265 if (this.editorcore.enableBlocks) {
33266 var fig = new Roo.htmleditor.BlockFigure({
33267 image_src : reader.result,
33269 caption_display : 'none' //default to hide captions..
33271 editor.insertAtCursor(fig.toHTML());
33272 this.addFiles(far, true);
33275 // just a standard img..
33276 if (sn && sn.tagName.toUpperCase() == 'IMG') {
33277 var oldSrc = sn.src;
33278 sn.src = reader.result;
33279 this.editor.syncValue();
33280 editor.owner.fireEvent('editorevent', editor.owner, false);
33281 editor.owner.fireEvent('imageupdate', editor.owner, sn, oldSrc);
33284 editor.insertAtCursor('<img src="' + reader.result +'">');
33285 this.addFiles(far, true);
33287 }).createDelegate(this));
33288 reader.readAsDataURL(f);
33294 onBtnClick : function(id)
33296 this.editorcore.relayCmd(id);
33297 this.editorcore.focus();
33300 onLinkClick : function(btn) {
33301 var url = this.selectedNode && this.selectedNode.tagName.toUpperCase() == 'A' ?
33302 this.selectedNode.getAttribute('href') : '';
33304 Roo.bootstrap.MessageBox.show({
33305 title : "Add / Edit Link URL",
33306 msg : "Enter the URL for the link",
33307 buttons: Roo.bootstrap.MessageBox.OKCANCEL,
33314 fn: function(pressed, newurl) {
33315 if (pressed != 'ok') {
33316 this.editorcore.focus();
33320 this.selectedNode.setAttribute('href', newurl);
33321 this.editor.syncValue();
33324 if(newurl && newurl .match(/http(s):\/\/.+/)) {
33325 this.editorcore.relayCmd('createlink', newurl);
33327 this.editorcore.focus();
33329 cls : this.editorcore.linkDialogCls
33333 * Protected method that will not generally be called directly. It triggers
33334 * a toolbar update by reading the markup state of the current selection in the editor.
33336 updateToolbar: function(editor ,ev, sel){
33338 if(!this.editorcore.activated){
33339 this.editor.onFirstFocus(); // is this neeed?
33343 var btns = this.buttons;
33344 var doc = this.editorcore.doc;
33345 var hasToggle = false;
33346 btns.each(function(e) {
33347 if (e.enableToggle && e.cmd) {
33348 hasToggle = hasToggle || (['align-left', 'align-right', 'align-center', 'image' , 'link', 'underline'].indexOf(e.btnid) < 0 && doc.queryCommandState(e.cmd));
33349 e.setActive(doc.queryCommandState(e.cmd));
33355 (ev.type == 'mouseup' || ev.type == 'click' ) &&
33356 ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
33357 // they have click on an image...
33358 // let's see if we can change the selection...
33363 var ans = this.editorcore.getAllAncestors();
33365 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
33366 sel = sel ? sel : this.editorcore.doc.body;
33367 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
33371 var lastSel = this.selectedNode;
33372 this.selectedNode = sel;
33374 // ok see if we are editing a block?
33377 // you are not actually selecting the block.
33378 if (sel && sel.hasAttribute('data-block')) {
33380 } else if (sel && sel.closest('[data-block]')) {
33381 db = sel.closest('[data-block]');
33384 Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
33385 e.classList.remove('roo-ed-selection');
33389 if (db && this.editorcore.enableBlocks) {
33390 block = Roo.htmleditor.Block.factory(db);
33393 db.className = (db.classList.length > 0 ? db.className + ' ' : '') +
33394 ' roo-ed-selection';
33395 sel = this.selectedNode = db;
33399 // highlight the 'a'..
33400 var tn = sel && sel.tagName.toUpperCase() || '';
33401 if (!block && sel && tn != 'A') {
33402 var asel = sel.closest('A');
33408 btns.get('link').setActive(tn == 'A' && this.selectedNode.hasAttribute('href'));
33409 btns.get('image').setActive(tn == 'IMG' || this.editorcore.enableBlocks && tn == 'FIGURE');
33410 btns.get('underline').setActive(tn == 'U' || sel.closest('u') ? true : false);
33412 Roo.bootstrap.menu.Manager.hideAll();
33418 // handle delete button..
33419 if (hasToggle || (tn.length && tn == 'BODY')) {
33420 this.deleteBtn.hide();
33424 this.deleteBtn.show();
33428 //this.editorsyncValue();
33430 onFirstFocus: function() {
33431 this.buttons.each(function(item){
33436 onDelete : function()
33438 var range = this.editorcore.createRange();
33439 var selection = this.editorcore.getSelection();
33440 var sn = this.selectedNode;
33441 range.setStart(sn,0);
33442 range.setEnd(sn,0);
33445 if (sn.hasAttribute('data-block')) {
33446 var block = Roo.htmleditor.Block.factory(this.selectedNode);
33448 sn = block.removeNode();
33449 sn.parentNode.removeChild(sn);
33450 selection.removeAllRanges();
33451 selection.addRange(range);
33452 this.updateToolbar(null, null, null);
33453 if (sn.tagName.toUpperCase() == 'FIGURE') {
33454 this.editor.syncValue();
33455 this.editor.fireEvent('imagedelete', this.editor, sn);
33458 this.selectedNode = false;
33459 this.editorcore.fireEditorEvent(false);
33465 return; // should not really happen..
33467 if (sn && sn.tagName == 'BODY') {
33470 var stn = sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
33472 // remove and keep parents.
33473 a = new Roo.htmleditor.FilterKeepChildren({tag : false});
33476 selection.removeAllRanges();
33477 selection.addRange(range);
33478 if (sn.tagName.toUpperCase() == 'IMG"') {
33479 this.editor.syncValue();
33480 this.editor.fireEvent('imagedelete', this.editor, sn);
33483 this.selectedNode = false;
33484 this.editorcore.fireEditorEvent(false);
33490 toggleSourceEdit : function(sourceEditMode){
33493 if(sourceEditMode){
33494 Roo.log("disabling buttons");
33495 this.buttons.each( function(item){
33496 if(item.cmd != 'pencil'){
33502 Roo.log("enabling buttons");
33503 if(this.editorcore.initialized){
33504 this.buttons.each( function(item){
33510 Roo.log("calling toggole on editor");
33511 // tell the editor that it's been pressed..
33512 this.editor.toggleSourceEdit(sourceEditMode);
33526 * @class Roo.bootstrap.form.Markdown
33527 * @extends Roo.bootstrap.form.TextArea
33528 * Bootstrap Showdown editable area
33529 * @cfg {string} content
33532 * Create a new Showdown
33535 Roo.bootstrap.form.Markdown = function(config){
33536 Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
33540 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea, {
33544 initEvents : function()
33547 Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
33548 this.markdownEl = this.el.createChild({
33549 cls : 'roo-markdown-area'
33551 this.inputEl().addClass('d-none');
33552 if (this.getValue() == '') {
33553 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33556 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33558 this.markdownEl.on('click', this.toggleTextEdit, this);
33559 this.on('blur', this.toggleTextEdit, this);
33560 this.on('specialkey', this.resizeTextArea, this);
33563 toggleTextEdit : function()
33565 var sh = this.markdownEl.getHeight();
33566 this.inputEl().addClass('d-none');
33567 this.markdownEl.addClass('d-none');
33568 if (!this.editing) {
33570 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
33571 this.inputEl().removeClass('d-none');
33572 this.inputEl().focus();
33573 this.editing = true;
33576 // show showdown...
33577 this.updateMarkdown();
33578 this.markdownEl.removeClass('d-none');
33579 this.editing = false;
33582 updateMarkdown : function()
33584 if (this.getValue() == '') {
33585 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33589 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33592 resizeTextArea: function () {
33595 Roo.log([sh, this.getValue().split("\n").length * 30]);
33596 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
33598 setValue : function(val)
33600 Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
33601 if (!this.editing) {
33602 this.updateMarkdown();
33608 if (!this.editing) {
33609 this.toggleTextEdit();
33617 * Ext JS Library 1.1.1
33618 * Copyright(c) 2006-2007, Ext JS, LLC.
33620 * Originally Released Under LGPL - original licence link has changed is not relivant.
33623 * <script type="text/javascript">
33627 * @class Roo.bootstrap.PagingToolbar
33628 * @extends Roo.bootstrap.nav.Simplebar
33629 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
33631 * Create a new PagingToolbar
33632 * @param {Object} config The config object
33633 * @param {Roo.data.Store} store
33635 Roo.bootstrap.PagingToolbar = function(config)
33637 // old args format still supported... - xtype is prefered..
33638 // created from xtype...
33640 this.ds = config.dataSource;
33642 if (config.store && !this.ds) {
33643 this.store= Roo.factory(config.store, Roo.data);
33644 this.ds = this.store;
33645 this.ds.xmodule = this.xmodule || false;
33648 this.toolbarItems = [];
33649 if (config.items) {
33650 this.toolbarItems = config.items;
33653 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
33658 this.bind(this.ds);
33661 if (Roo.bootstrap.version == 4) {
33662 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
33664 this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
33669 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
33671 * @cfg {Roo.bootstrap.Button} buttons[]
33672 * Buttons for the toolbar
33675 * @cfg {Roo.data.Store} store
33676 * The underlying data store providing the paged data
33679 * @cfg {String/HTMLElement/Element} container
33680 * container The id or element that will contain the toolbar
33683 * @cfg {Boolean} displayInfo
33684 * True to display the displayMsg (defaults to false)
33687 * @cfg {Number} pageSize
33688 * The number of records to display per page (defaults to 20)
33692 * @cfg {String} displayMsg
33693 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
33695 displayMsg : 'Displaying {0} - {1} of {2}',
33697 * @cfg {String} emptyMsg
33698 * The message to display when no records are found (defaults to "No data to display")
33700 emptyMsg : 'No data to display',
33702 * Customizable piece of the default paging text (defaults to "Page")
33705 beforePageText : "Page",
33707 * Customizable piece of the default paging text (defaults to "of %0")
33710 afterPageText : "of {0}",
33712 * Customizable piece of the default paging text (defaults to "First Page")
33715 firstText : "First Page",
33717 * Customizable piece of the default paging text (defaults to "Previous Page")
33720 prevText : "Previous Page",
33722 * Customizable piece of the default paging text (defaults to "Next Page")
33725 nextText : "Next Page",
33727 * Customizable piece of the default paging text (defaults to "Last Page")
33730 lastText : "Last Page",
33732 * Customizable piece of the default paging text (defaults to "Refresh")
33735 refreshText : "Refresh",
33739 onRender : function(ct, position)
33741 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
33742 this.navgroup.parentId = this.id;
33743 this.navgroup.onRender(this.el, null);
33744 // add the buttons to the navgroup
33746 if(this.displayInfo){
33747 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
33748 this.displayEl = this.el.select('.x-paging-info', true).first();
33749 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
33750 // this.displayEl = navel.el.select('span',true).first();
33756 Roo.each(_this.buttons, function(e){ // this might need to use render????
33757 Roo.factory(e).render(_this.el);
33761 Roo.each(_this.toolbarItems, function(e) {
33762 _this.navgroup.addItem(e);
33766 this.first = this.navgroup.addItem({
33767 tooltip: this.firstText,
33768 cls: "prev btn-outline-secondary",
33769 html : ' <i class="fa fa-step-backward"></i>',
33771 preventDefault: true,
33772 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
33775 this.prev = this.navgroup.addItem({
33776 tooltip: this.prevText,
33777 cls: "prev btn-outline-secondary",
33778 html : ' <i class="fa fa-backward"></i>',
33780 preventDefault: true,
33781 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
33783 //this.addSeparator();
33786 var field = this.navgroup.addItem( {
33788 cls : 'x-paging-position btn-outline-secondary',
33790 html : this.beforePageText +
33791 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
33792 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
33795 this.field = field.el.select('input', true).first();
33796 this.field.on("keydown", this.onPagingKeydown, this);
33797 this.field.on("focus", function(){this.dom.select();});
33800 this.afterTextEl = field.el.select('.x-paging-after',true).first();
33801 //this.field.setHeight(18);
33802 //this.addSeparator();
33803 this.next = this.navgroup.addItem({
33804 tooltip: this.nextText,
33805 cls: "next btn-outline-secondary",
33806 html : ' <i class="fa fa-forward"></i>',
33808 preventDefault: true,
33809 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
33811 this.last = this.navgroup.addItem({
33812 tooltip: this.lastText,
33813 html : ' <i class="fa fa-step-forward"></i>',
33814 cls: "next btn-outline-secondary",
33816 preventDefault: true,
33817 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
33819 //this.addSeparator();
33820 this.loading = this.navgroup.addItem({
33821 tooltip: this.refreshText,
33822 cls: "btn-outline-secondary",
33823 html : ' <i class="fa fa-refresh"></i>',
33824 preventDefault: true,
33825 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
33831 updateInfo : function(){
33832 if(this.displayEl){
33833 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
33834 var msg = count == 0 ?
33838 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
33840 this.displayEl.update(msg);
33845 onLoad : function(ds, r, o)
33847 this.cursor = o.params && o.params.start ? o.params.start : 0;
33849 var d = this.getPageData(),
33854 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
33855 this.field.dom.value = ap;
33856 this.first.setDisabled(ap == 1);
33857 this.prev.setDisabled(ap == 1);
33858 this.next.setDisabled(ap == ps);
33859 this.last.setDisabled(ap == ps);
33860 this.loading.enable();
33865 getPageData : function(){
33866 var total = this.ds.getTotalCount();
33869 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
33870 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
33875 onLoadError : function(proxy, o){
33876 this.loading.enable();
33877 if (this.ds.events.loadexception.listeners.length < 2) {
33878 // nothing has been assigned to loadexception except this...
33880 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
33886 onPagingKeydown : function(e){
33887 var k = e.getKey();
33888 var d = this.getPageData();
33890 var v = this.field.dom.value, pageNum;
33891 if(!v || isNaN(pageNum = parseInt(v, 10))){
33892 this.field.dom.value = d.activePage;
33895 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
33896 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33899 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))
33901 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
33902 this.field.dom.value = pageNum;
33903 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
33906 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
33908 var v = this.field.dom.value, pageNum;
33909 var increment = (e.shiftKey) ? 10 : 1;
33910 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
33913 if(!v || isNaN(pageNum = parseInt(v, 10))) {
33914 this.field.dom.value = d.activePage;
33917 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
33919 this.field.dom.value = parseInt(v, 10) + increment;
33920 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
33921 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33928 beforeLoad : function(){
33930 this.loading.disable();
33935 onClick : function(which){
33944 ds.load({params:{start: 0, limit: this.pageSize}});
33947 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33950 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33953 var total = ds.getTotalCount();
33954 var extra = total % this.pageSize;
33955 var lastStart = extra ? (total - extra) : total-this.pageSize;
33956 ds.load({params:{start: lastStart, limit: this.pageSize}});
33959 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33965 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33966 * @param {Roo.data.Store} store The data store to unbind
33968 unbind : function(ds){
33969 ds.un("beforeload", this.beforeLoad, this);
33970 ds.un("load", this.onLoad, this);
33971 ds.un("loadexception", this.onLoadError, this);
33972 ds.un("remove", this.updateInfo, this);
33973 ds.un("add", this.updateInfo, this);
33974 this.ds = undefined;
33978 * Binds the paging toolbar to the specified {@link Roo.data.Store}
33979 * @param {Roo.data.Store} store The data store to bind
33981 bind : function(ds){
33982 ds.on("beforeload", this.beforeLoad, this);
33983 ds.on("load", this.onLoad, this);
33984 ds.on("loadexception", this.onLoadError, this);
33985 ds.on("remove", this.updateInfo, this);
33986 ds.on("add", this.updateInfo, this);
33997 * @class Roo.bootstrap.MessageBar
33998 * @extends Roo.bootstrap.Component
33999 * Bootstrap MessageBar class
34000 * @cfg {String} html contents of the MessageBar
34001 * @cfg {String} weight (info | success | warning | danger) default info
34002 * @cfg {String} beforeClass insert the bar before the given class
34003 * @cfg {Boolean} closable (true | false) default false
34004 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
34007 * Create a new Element
34008 * @param {Object} config The config object
34011 Roo.bootstrap.MessageBar = function(config){
34012 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
34015 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
34021 beforeClass: 'bootstrap-sticky-wrap',
34023 getAutoCreate : function(){
34027 cls: 'alert alert-dismissable alert-' + this.weight,
34032 html: this.html || ''
34038 cfg.cls += ' alert-messages-fixed';
34052 onRender : function(ct, position)
34054 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
34057 var cfg = Roo.apply({}, this.getAutoCreate());
34061 cfg.cls += ' ' + this.cls;
34064 cfg.style = this.style;
34066 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
34068 this.el.setVisibilityMode(Roo.Element.DISPLAY);
34071 this.el.select('>button.close').on('click', this.hide, this);
34077 if (!this.rendered) {
34083 this.fireEvent('show', this);
34089 if (!this.rendered) {
34095 this.fireEvent('hide', this);
34098 update : function()
34100 // var e = this.el.dom.firstChild;
34102 // if(this.closable){
34103 // e = e.nextSibling;
34106 // e.data = this.html || '';
34108 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
34124 * @class Roo.bootstrap.Graph
34125 * @extends Roo.bootstrap.Component
34126 * Bootstrap Graph class
34130 @cfg {String} graphtype bar | vbar | pie
34131 @cfg {number} g_x coodinator | centre x (pie)
34132 @cfg {number} g_y coodinator | centre y (pie)
34133 @cfg {number} g_r radius (pie)
34134 @cfg {number} g_height height of the chart (respected by all elements in the set)
34135 @cfg {number} g_width width of the chart (respected by all elements in the set)
34136 @cfg {Object} title The title of the chart
34139 -opts (object) options for the chart
34141 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
34142 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
34144 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.
34145 o stacked (boolean) whether or not to tread values as in a stacked bar chart
34147 o stretch (boolean)
34149 -opts (object) options for the pie
34152 o startAngle (number)
34153 o endAngle (number)
34157 * Create a new Input
34158 * @param {Object} config The config object
34161 Roo.bootstrap.Graph = function(config){
34162 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
34168 * The img click event for the img.
34169 * @param {Roo.EventObject} e
34175 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
34186 //g_colors: this.colors,
34193 getAutoCreate : function(){
34204 onRender : function(ct,position){
34207 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
34209 if (typeof(Raphael) == 'undefined') {
34210 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
34214 this.raphael = Raphael(this.el.dom);
34216 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34217 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34218 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34219 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
34221 r.text(160, 10, "Single Series Chart").attr(txtattr);
34222 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
34223 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
34224 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
34226 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
34227 r.barchart(330, 10, 300, 220, data1);
34228 r.barchart(10, 250, 300, 220, data2, {stacked: true});
34229 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
34232 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
34233 // r.barchart(30, 30, 560, 250, xdata, {
34234 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
34235 // axis : "0 0 1 1",
34236 // axisxlabels : xdata
34237 // //yvalues : cols,
34240 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
34242 // this.load(null,xdata,{
34243 // axis : "0 0 1 1",
34244 // axisxlabels : xdata
34249 load : function(graphtype,xdata,opts)
34251 this.raphael.clear();
34253 graphtype = this.graphtype;
34258 var r = this.raphael,
34259 fin = function () {
34260 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
34262 fout = function () {
34263 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
34265 pfin = function() {
34266 this.sector.stop();
34267 this.sector.scale(1.1, 1.1, this.cx, this.cy);
34270 this.label[0].stop();
34271 this.label[0].attr({ r: 7.5 });
34272 this.label[1].attr({ "font-weight": 800 });
34275 pfout = function() {
34276 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
34279 this.label[0].animate({ r: 5 }, 500, "bounce");
34280 this.label[1].attr({ "font-weight": 400 });
34286 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
34289 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
34292 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
34293 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
34295 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
34302 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
34307 setTitle: function(o)
34312 initEvents: function() {
34315 this.el.on('click', this.onClick, this);
34319 onClick : function(e)
34321 Roo.log('img onclick');
34322 this.fireEvent('click', this, e);
34328 Roo.bootstrap.dash = {};/*
34334 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34337 * @class Roo.bootstrap.dash.NumberBox
34338 * @extends Roo.bootstrap.Component
34339 * Bootstrap NumberBox class
34340 * @cfg {String} headline Box headline
34341 * @cfg {String} content Box content
34342 * @cfg {String} icon Box icon
34343 * @cfg {String} footer Footer text
34344 * @cfg {String} fhref Footer href
34347 * Create a new NumberBox
34348 * @param {Object} config The config object
34352 Roo.bootstrap.dash.NumberBox = function(config){
34353 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
34357 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
34366 getAutoCreate : function(){
34370 cls : 'small-box ',
34378 cls : 'roo-headline',
34379 html : this.headline
34383 cls : 'roo-content',
34384 html : this.content
34398 cls : 'ion ' + this.icon
34407 cls : 'small-box-footer',
34408 href : this.fhref || '#',
34412 cfg.cn.push(footer);
34419 onRender : function(ct,position){
34420 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
34427 setHeadline: function (value)
34429 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
34432 setFooter: function (value, href)
34434 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
34437 this.el.select('a.small-box-footer',true).first().attr('href', href);
34442 setContent: function (value)
34444 this.el.select('.roo-content',true).first().dom.innerHTML = value;
34447 initEvents: function()
34461 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34464 * @class Roo.bootstrap.dash.TabBox
34465 * @extends Roo.bootstrap.Component
34466 * @children Roo.bootstrap.dash.TabPane
34467 * Bootstrap TabBox class
34468 * @cfg {String} title Title of the TabBox
34469 * @cfg {String} icon Icon of the TabBox
34470 * @cfg {Boolean} showtabs (true|false) show the tabs default true
34471 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
34474 * Create a new TabBox
34475 * @param {Object} config The config object
34479 Roo.bootstrap.dash.TabBox = function(config){
34480 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
34485 * When a pane is added
34486 * @param {Roo.bootstrap.dash.TabPane} pane
34490 * @event activatepane
34491 * When a pane is activated
34492 * @param {Roo.bootstrap.dash.TabPane} pane
34494 "activatepane" : true
34502 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
34507 tabScrollable : false,
34509 getChildContainer : function()
34511 return this.el.select('.tab-content', true).first();
34514 getAutoCreate : function(){
34518 cls: 'pull-left header',
34526 cls: 'fa ' + this.icon
34532 cls: 'nav nav-tabs pull-right',
34538 if(this.tabScrollable){
34545 cls: 'nav nav-tabs pull-right',
34556 cls: 'nav-tabs-custom',
34561 cls: 'tab-content no-padding',
34569 initEvents : function()
34571 //Roo.log('add add pane handler');
34572 this.on('addpane', this.onAddPane, this);
34575 * Updates the box title
34576 * @param {String} html to set the title to.
34578 setTitle : function(value)
34580 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
34582 onAddPane : function(pane)
34584 this.panes.push(pane);
34585 //Roo.log('addpane');
34587 // tabs are rendere left to right..
34588 if(!this.showtabs){
34592 var ctr = this.el.select('.nav-tabs', true).first();
34595 var existing = ctr.select('.nav-tab',true);
34596 var qty = existing.getCount();;
34599 var tab = ctr.createChild({
34601 cls : 'nav-tab' + (qty ? '' : ' active'),
34609 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
34612 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
34614 pane.el.addClass('active');
34619 onTabClick : function(ev,un,ob,pane)
34621 //Roo.log('tab - prev default');
34622 ev.preventDefault();
34625 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
34626 pane.tab.addClass('active');
34627 //Roo.log(pane.title);
34628 this.getChildContainer().select('.tab-pane',true).removeClass('active');
34629 // technically we should have a deactivate event.. but maybe add later.
34630 // and it should not de-activate the selected tab...
34631 this.fireEvent('activatepane', pane);
34632 pane.el.addClass('active');
34633 pane.fireEvent('activate');
34638 getActivePane : function()
34641 Roo.each(this.panes, function(p) {
34642 if(p.el.hasClass('active')){
34663 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34665 * @class Roo.bootstrap.TabPane
34666 * @extends Roo.bootstrap.Component
34667 * @children Roo.bootstrap.Graph Roo.bootstrap.Column
34668 * Bootstrap TabPane class
34669 * @cfg {Boolean} active (false | true) Default false
34670 * @cfg {String} title title of panel
34674 * Create a new TabPane
34675 * @param {Object} config The config object
34678 Roo.bootstrap.dash.TabPane = function(config){
34679 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
34685 * When a pane is activated
34686 * @param {Roo.bootstrap.dash.TabPane} pane
34693 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
34698 // the tabBox that this is attached to.
34701 getAutoCreate : function()
34709 cfg.cls += ' active';
34714 initEvents : function()
34716 //Roo.log('trigger add pane handler');
34717 this.parent().fireEvent('addpane', this)
34721 * Updates the tab title
34722 * @param {String} html to set the title to.
34724 setTitle: function(str)
34730 this.tab.select('a', true).first().dom.innerHTML = str;
34749 * @class Roo.bootstrap.Tooltip
34750 * Bootstrap Tooltip class
34751 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
34752 * to determine which dom element triggers the tooltip.
34754 * It needs to add support for additional attributes like tooltip-position
34757 * Create a new Toolti
34758 * @param {Object} config The config object
34761 Roo.bootstrap.Tooltip = function(config){
34762 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
34764 this.alignment = Roo.bootstrap.Tooltip.alignment;
34766 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
34767 this.alignment = config.alignment;
34772 Roo.apply(Roo.bootstrap.Tooltip, {
34774 * @function init initialize tooltip monitoring.
34778 currentTip : false,
34779 currentRegion : false,
34785 Roo.get(document).on('mouseover', this.enter ,this);
34786 Roo.get(document).on('mouseout', this.leave, this);
34789 this.currentTip = new Roo.bootstrap.Tooltip();
34792 enter : function(ev)
34794 var dom = ev.getTarget();
34796 //Roo.log(['enter',dom]);
34797 var el = Roo.fly(dom);
34798 if (this.currentEl) {
34800 //Roo.log(this.currentEl);
34801 //Roo.log(this.currentEl.contains(dom));
34802 if (this.currentEl == el) {
34805 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
34811 if (this.currentTip.el) {
34812 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
34816 if(!el || el.dom == document){
34822 if (!el.attr('tooltip')) {
34823 pel = el.findParent("[tooltip]");
34825 bindEl = Roo.get(pel);
34831 // you can not look for children, as if el is the body.. then everythign is the child..
34832 if (!pel && !el.attr('tooltip')) { //
34833 if (!el.select("[tooltip]").elements.length) {
34836 // is the mouse over this child...?
34837 bindEl = el.select("[tooltip]").first();
34838 var xy = ev.getXY();
34839 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
34840 //Roo.log("not in region.");
34843 //Roo.log("child element over..");
34846 this.currentEl = el;
34847 this.currentTip.bind(bindEl);
34848 this.currentRegion = Roo.lib.Region.getRegion(dom);
34849 this.currentTip.enter();
34852 leave : function(ev)
34854 var dom = ev.getTarget();
34855 //Roo.log(['leave',dom]);
34856 if (!this.currentEl) {
34861 if (dom != this.currentEl.dom) {
34864 var xy = ev.getXY();
34865 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
34868 // only activate leave if mouse cursor is outside... bounding box..
34873 if (this.currentTip) {
34874 this.currentTip.leave();
34876 //Roo.log('clear currentEl');
34877 this.currentEl = false;
34882 'left' : ['r-l', [-2,0], 'right'],
34883 'right' : ['l-r', [2,0], 'left'],
34884 'bottom' : ['t-b', [0,2], 'top'],
34885 'top' : [ 'b-t', [0,-2], 'bottom']
34891 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
34896 delay : null, // can be { show : 300 , hide: 500}
34900 hoverState : null, //???
34902 placement : 'bottom',
34906 getAutoCreate : function(){
34913 cls : 'tooltip-arrow arrow'
34916 cls : 'tooltip-inner'
34923 bind : function(el)
34928 initEvents : function()
34930 this.arrowEl = this.el.select('.arrow', true).first();
34931 this.innerEl = this.el.select('.tooltip-inner', true).first();
34934 enter : function () {
34936 if (this.timeout != null) {
34937 clearTimeout(this.timeout);
34940 this.hoverState = 'in';
34941 //Roo.log("enter - show");
34942 if (!this.delay || !this.delay.show) {
34947 this.timeout = setTimeout(function () {
34948 if (_t.hoverState == 'in') {
34951 }, this.delay.show);
34955 clearTimeout(this.timeout);
34957 this.hoverState = 'out';
34958 if (!this.delay || !this.delay.hide) {
34964 this.timeout = setTimeout(function () {
34965 //Roo.log("leave - timeout");
34967 if (_t.hoverState == 'out') {
34969 Roo.bootstrap.Tooltip.currentEl = false;
34974 show : function (msg)
34977 this.render(document.body);
34980 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34982 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34984 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34986 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34987 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34989 if(this.bindEl.attr('tooltip-class')) {
34990 this.el.addClass(this.bindEl.attr('tooltip-class'));
34993 var placement = typeof this.placement == 'function' ?
34994 this.placement.call(this, this.el, on_el) :
34997 if(this.bindEl.attr('tooltip-placement')) {
34998 placement = this.bindEl.attr('tooltip-placement');
35001 var autoToken = /\s?auto?\s?/i;
35002 var autoPlace = autoToken.test(placement);
35004 placement = placement.replace(autoToken, '') || 'top';
35008 //this.el.setXY([0,0]);
35010 //this.el.dom.style.display='block';
35012 //this.el.appendTo(on_el);
35014 var p = this.getPosition();
35015 var box = this.el.getBox();
35021 var align = this.alignment[placement];
35023 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
35025 if(placement == 'top' || placement == 'bottom'){
35027 placement = 'right';
35030 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
35031 placement = 'left';
35034 var scroll = Roo.select('body', true).first().getScroll();
35036 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
35040 align = this.alignment[placement];
35042 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
35046 var elems = document.getElementsByTagName('div');
35047 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
35048 for (var i = 0; i < elems.length; i++) {
35049 var zindex = Number.parseInt(
35050 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
35053 if (zindex > highest) {
35060 this.el.dom.style.zIndex = highest;
35062 this.el.alignTo(this.bindEl, align[0],align[1]);
35063 //var arrow = this.el.select('.arrow',true).first();
35064 //arrow.set(align[2],
35066 this.el.addClass(placement);
35067 this.el.addClass("bs-tooltip-"+ placement);
35069 this.el.addClass('in fade show');
35071 this.hoverState = null;
35073 if (this.el.hasClass('fade')) {
35088 //this.el.setXY([0,0]);
35089 if(this.bindEl.attr('tooltip-class')) {
35090 this.el.removeClass(this.bindEl.attr('tooltip-class'));
35092 this.el.removeClass(['show', 'in']);
35108 * @class Roo.bootstrap.LocationPicker
35109 * @extends Roo.bootstrap.Component
35110 * Bootstrap LocationPicker class
35111 * @cfg {Number} latitude Position when init default 0
35112 * @cfg {Number} longitude Position when init default 0
35113 * @cfg {Number} zoom default 15
35114 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
35115 * @cfg {Boolean} mapTypeControl default false
35116 * @cfg {Boolean} disableDoubleClickZoom default false
35117 * @cfg {Boolean} scrollwheel default true
35118 * @cfg {Boolean} streetViewControl default false
35119 * @cfg {Number} radius default 0
35120 * @cfg {String} locationName
35121 * @cfg {Boolean} draggable default true
35122 * @cfg {Boolean} enableAutocomplete default false
35123 * @cfg {Boolean} enableReverseGeocode default true
35124 * @cfg {String} markerTitle
35127 * Create a new LocationPicker
35128 * @param {Object} config The config object
35132 Roo.bootstrap.LocationPicker = function(config){
35134 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
35139 * Fires when the picker initialized.
35140 * @param {Roo.bootstrap.LocationPicker} this
35141 * @param {Google Location} location
35145 * @event positionchanged
35146 * Fires when the picker position changed.
35147 * @param {Roo.bootstrap.LocationPicker} this
35148 * @param {Google Location} location
35150 positionchanged : true,
35153 * Fires when the map resize.
35154 * @param {Roo.bootstrap.LocationPicker} this
35159 * Fires when the map show.
35160 * @param {Roo.bootstrap.LocationPicker} this
35165 * Fires when the map hide.
35166 * @param {Roo.bootstrap.LocationPicker} this
35171 * Fires when click the map.
35172 * @param {Roo.bootstrap.LocationPicker} this
35173 * @param {Map event} e
35177 * @event mapRightClick
35178 * Fires when right click the map.
35179 * @param {Roo.bootstrap.LocationPicker} this
35180 * @param {Map event} e
35182 mapRightClick : true,
35184 * @event markerClick
35185 * Fires when click the marker.
35186 * @param {Roo.bootstrap.LocationPicker} this
35187 * @param {Map event} e
35189 markerClick : true,
35191 * @event markerRightClick
35192 * Fires when right click the marker.
35193 * @param {Roo.bootstrap.LocationPicker} this
35194 * @param {Map event} e
35196 markerRightClick : true,
35198 * @event OverlayViewDraw
35199 * Fires when OverlayView Draw
35200 * @param {Roo.bootstrap.LocationPicker} this
35202 OverlayViewDraw : true,
35204 * @event OverlayViewOnAdd
35205 * Fires when OverlayView Draw
35206 * @param {Roo.bootstrap.LocationPicker} this
35208 OverlayViewOnAdd : true,
35210 * @event OverlayViewOnRemove
35211 * Fires when OverlayView Draw
35212 * @param {Roo.bootstrap.LocationPicker} this
35214 OverlayViewOnRemove : true,
35216 * @event OverlayViewShow
35217 * Fires when OverlayView Draw
35218 * @param {Roo.bootstrap.LocationPicker} this
35219 * @param {Pixel} cpx
35221 OverlayViewShow : true,
35223 * @event OverlayViewHide
35224 * Fires when OverlayView Draw
35225 * @param {Roo.bootstrap.LocationPicker} this
35227 OverlayViewHide : true,
35229 * @event loadexception
35230 * Fires when load google lib failed.
35231 * @param {Roo.bootstrap.LocationPicker} this
35233 loadexception : true
35238 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
35240 gMapContext: false,
35246 mapTypeControl: false,
35247 disableDoubleClickZoom: false,
35249 streetViewControl: false,
35253 enableAutocomplete: false,
35254 enableReverseGeocode: true,
35257 getAutoCreate: function()
35262 cls: 'roo-location-picker'
35268 initEvents: function(ct, position)
35270 if(!this.el.getWidth() || this.isApplied()){
35274 this.el.setVisibilityMode(Roo.Element.DISPLAY);
35279 initial: function()
35281 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
35282 this.fireEvent('loadexception', this);
35286 if(!this.mapTypeId){
35287 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
35290 this.gMapContext = this.GMapContext();
35292 this.initOverlayView();
35294 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
35298 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
35299 _this.setPosition(_this.gMapContext.marker.position);
35302 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
35303 _this.fireEvent('mapClick', this, event);
35307 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
35308 _this.fireEvent('mapRightClick', this, event);
35312 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
35313 _this.fireEvent('markerClick', this, event);
35317 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
35318 _this.fireEvent('markerRightClick', this, event);
35322 this.setPosition(this.gMapContext.location);
35324 this.fireEvent('initial', this, this.gMapContext.location);
35327 initOverlayView: function()
35331 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
35335 _this.fireEvent('OverlayViewDraw', _this);
35340 _this.fireEvent('OverlayViewOnAdd', _this);
35343 onRemove: function()
35345 _this.fireEvent('OverlayViewOnRemove', _this);
35348 show: function(cpx)
35350 _this.fireEvent('OverlayViewShow', _this, cpx);
35355 _this.fireEvent('OverlayViewHide', _this);
35361 fromLatLngToContainerPixel: function(event)
35363 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
35366 isApplied: function()
35368 return this.getGmapContext() == false ? false : true;
35371 getGmapContext: function()
35373 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
35376 GMapContext: function()
35378 var position = new google.maps.LatLng(this.latitude, this.longitude);
35380 var _map = new google.maps.Map(this.el.dom, {
35383 mapTypeId: this.mapTypeId,
35384 mapTypeControl: this.mapTypeControl,
35385 disableDoubleClickZoom: this.disableDoubleClickZoom,
35386 scrollwheel: this.scrollwheel,
35387 streetViewControl: this.streetViewControl,
35388 locationName: this.locationName,
35389 draggable: this.draggable,
35390 enableAutocomplete: this.enableAutocomplete,
35391 enableReverseGeocode: this.enableReverseGeocode
35394 var _marker = new google.maps.Marker({
35395 position: position,
35397 title: this.markerTitle,
35398 draggable: this.draggable
35405 location: position,
35406 radius: this.radius,
35407 locationName: this.locationName,
35408 addressComponents: {
35409 formatted_address: null,
35410 addressLine1: null,
35411 addressLine2: null,
35413 streetNumber: null,
35417 stateOrProvince: null
35420 domContainer: this.el.dom,
35421 geodecoder: new google.maps.Geocoder()
35425 drawCircle: function(center, radius, options)
35427 if (this.gMapContext.circle != null) {
35428 this.gMapContext.circle.setMap(null);
35432 options = Roo.apply({}, options, {
35433 strokeColor: "#0000FF",
35434 strokeOpacity: .35,
35436 fillColor: "#0000FF",
35440 options.map = this.gMapContext.map;
35441 options.radius = radius;
35442 options.center = center;
35443 this.gMapContext.circle = new google.maps.Circle(options);
35444 return this.gMapContext.circle;
35450 setPosition: function(location)
35452 this.gMapContext.location = location;
35453 this.gMapContext.marker.setPosition(location);
35454 this.gMapContext.map.panTo(location);
35455 this.drawCircle(location, this.gMapContext.radius, {});
35459 if (this.gMapContext.settings.enableReverseGeocode) {
35460 this.gMapContext.geodecoder.geocode({
35461 latLng: this.gMapContext.location
35462 }, function(results, status) {
35464 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
35465 _this.gMapContext.locationName = results[0].formatted_address;
35466 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
35468 _this.fireEvent('positionchanged', this, location);
35475 this.fireEvent('positionchanged', this, location);
35480 google.maps.event.trigger(this.gMapContext.map, "resize");
35482 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
35484 this.fireEvent('resize', this);
35487 setPositionByLatLng: function(latitude, longitude)
35489 this.setPosition(new google.maps.LatLng(latitude, longitude));
35492 getCurrentPosition: function()
35495 latitude: this.gMapContext.location.lat(),
35496 longitude: this.gMapContext.location.lng()
35500 getAddressName: function()
35502 return this.gMapContext.locationName;
35505 getAddressComponents: function()
35507 return this.gMapContext.addressComponents;
35510 address_component_from_google_geocode: function(address_components)
35514 for (var i = 0; i < address_components.length; i++) {
35515 var component = address_components[i];
35516 if (component.types.indexOf("postal_code") >= 0) {
35517 result.postalCode = component.short_name;
35518 } else if (component.types.indexOf("street_number") >= 0) {
35519 result.streetNumber = component.short_name;
35520 } else if (component.types.indexOf("route") >= 0) {
35521 result.streetName = component.short_name;
35522 } else if (component.types.indexOf("neighborhood") >= 0) {
35523 result.city = component.short_name;
35524 } else if (component.types.indexOf("locality") >= 0) {
35525 result.city = component.short_name;
35526 } else if (component.types.indexOf("sublocality") >= 0) {
35527 result.district = component.short_name;
35528 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
35529 result.stateOrProvince = component.short_name;
35530 } else if (component.types.indexOf("country") >= 0) {
35531 result.country = component.short_name;
35535 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
35536 result.addressLine2 = "";
35540 setZoomLevel: function(zoom)
35542 this.gMapContext.map.setZoom(zoom);
35555 this.fireEvent('show', this);
35566 this.fireEvent('hide', this);
35571 Roo.apply(Roo.bootstrap.LocationPicker, {
35573 OverlayView : function(map, options)
35575 options = options || {};
35582 * @class Roo.bootstrap.Alert
35583 * @extends Roo.bootstrap.Component
35584 * Bootstrap Alert class - shows an alert area box
35586 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
35587 Enter a valid email address
35590 * @cfg {String} title The title of alert
35591 * @cfg {String} html The content of alert
35592 * @cfg {String} weight (success|info|warning|danger) Weight of the message
35593 * @cfg {String} fa font-awesomeicon
35594 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
35595 * @cfg {Boolean} close true to show a x closer
35599 * Create a new alert
35600 * @param {Object} config The config object
35604 Roo.bootstrap.Alert = function(config){
35605 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
35609 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
35615 faicon: false, // BC
35619 getAutoCreate : function()
35631 style : this.close ? '' : 'display:none'
35635 cls : 'roo-alert-icon'
35640 cls : 'roo-alert-title',
35645 cls : 'roo-alert-text',
35652 cfg.cn[0].cls += ' fa ' + this.faicon;
35655 cfg.cn[0].cls += ' fa ' + this.fa;
35659 cfg.cls += ' alert-' + this.weight;
35665 initEvents: function()
35667 this.el.setVisibilityMode(Roo.Element.DISPLAY);
35668 this.titleEl = this.el.select('.roo-alert-title',true).first();
35669 this.iconEl = this.el.select('.roo-alert-icon',true).first();
35670 this.htmlEl = this.el.select('.roo-alert-text',true).first();
35671 if (this.seconds > 0) {
35672 this.hide.defer(this.seconds, this);
35676 * Set the Title Message HTML
35677 * @param {String} html
35679 setTitle : function(str)
35681 this.titleEl.dom.innerHTML = str;
35685 * Set the Body Message HTML
35686 * @param {String} html
35688 setHtml : function(str)
35690 this.htmlEl.dom.innerHTML = str;
35693 * Set the Weight of the alert
35694 * @param {String} (success|info|warning|danger) weight
35697 setWeight : function(weight)
35700 this.el.removeClass('alert-' + this.weight);
35703 this.weight = weight;
35705 this.el.addClass('alert-' + this.weight);
35708 * Set the Icon of the alert
35709 * @param {String} see fontawsome names (name without the 'fa-' bit)
35711 setIcon : function(icon)
35714 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
35717 this.faicon = icon;
35719 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
35744 * @class Roo.bootstrap.UploadCropbox
35745 * @extends Roo.bootstrap.Component
35746 * Bootstrap UploadCropbox class
35747 * @cfg {String} emptyText show when image has been loaded
35748 * @cfg {String} rotateNotify show when image too small to rotate
35749 * @cfg {Number} errorTimeout default 3000
35750 * @cfg {Number} minWidth default 300
35751 * @cfg {Number} minHeight default 300
35752 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
35753 * @cfg {Boolean} isDocument (true|false) default false
35754 * @cfg {String} url action url
35755 * @cfg {String} paramName default 'imageUpload'
35756 * @cfg {String} method default POST
35757 * @cfg {Boolean} loadMask (true|false) default true
35758 * @cfg {Boolean} loadingText default 'Loading...'
35761 * Create a new UploadCropbox
35762 * @param {Object} config The config object
35765 Roo.bootstrap.UploadCropbox = function(config){
35766 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
35770 * @event beforeselectfile
35771 * Fire before select file
35772 * @param {Roo.bootstrap.UploadCropbox} this
35774 "beforeselectfile" : true,
35777 * Fire after initEvent
35778 * @param {Roo.bootstrap.UploadCropbox} this
35783 * Fire after initEvent
35784 * @param {Roo.bootstrap.UploadCropbox} this
35785 * @param {String} data
35790 * Fire when preparing the file data
35791 * @param {Roo.bootstrap.UploadCropbox} this
35792 * @param {Object} file
35797 * Fire when get exception
35798 * @param {Roo.bootstrap.UploadCropbox} this
35799 * @param {XMLHttpRequest} xhr
35801 "exception" : true,
35803 * @event beforeloadcanvas
35804 * Fire before load the canvas
35805 * @param {Roo.bootstrap.UploadCropbox} this
35806 * @param {String} src
35808 "beforeloadcanvas" : true,
35811 * Fire when trash image
35812 * @param {Roo.bootstrap.UploadCropbox} this
35817 * Fire when download the image
35818 * @param {Roo.bootstrap.UploadCropbox} this
35822 * @event footerbuttonclick
35823 * Fire when footerbuttonclick
35824 * @param {Roo.bootstrap.UploadCropbox} this
35825 * @param {String} type
35827 "footerbuttonclick" : true,
35831 * @param {Roo.bootstrap.UploadCropbox} this
35836 * Fire when rotate the image
35837 * @param {Roo.bootstrap.UploadCropbox} this
35838 * @param {String} pos
35843 * Fire when inspect the file
35844 * @param {Roo.bootstrap.UploadCropbox} this
35845 * @param {Object} file
35850 * Fire when xhr upload the file
35851 * @param {Roo.bootstrap.UploadCropbox} this
35852 * @param {Object} data
35857 * Fire when arrange the file data
35858 * @param {Roo.bootstrap.UploadCropbox} this
35859 * @param {Object} formData
35864 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
35867 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
35869 emptyText : 'Click to upload image',
35870 rotateNotify : 'Image is too small to rotate',
35871 errorTimeout : 3000,
35885 cropType : 'image/jpeg',
35887 canvasLoaded : false,
35888 isDocument : false,
35890 paramName : 'imageUpload',
35892 loadingText : 'Loading...',
35895 getAutoCreate : function()
35899 cls : 'roo-upload-cropbox',
35903 cls : 'roo-upload-cropbox-selector',
35908 cls : 'roo-upload-cropbox-body',
35909 style : 'cursor:pointer',
35913 cls : 'roo-upload-cropbox-preview'
35917 cls : 'roo-upload-cropbox-thumb'
35921 cls : 'roo-upload-cropbox-empty-notify',
35922 html : this.emptyText
35926 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
35927 html : this.rotateNotify
35933 cls : 'roo-upload-cropbox-footer',
35936 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
35946 onRender : function(ct, position)
35948 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35950 if (this.buttons.length) {
35952 Roo.each(this.buttons, function(bb) {
35954 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35956 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35962 this.maskEl = this.el;
35966 initEvents : function()
35968 this.urlAPI = (window.createObjectURL && window) ||
35969 (window.URL && URL.revokeObjectURL && URL) ||
35970 (window.webkitURL && webkitURL);
35972 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35973 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35975 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35976 this.selectorEl.hide();
35978 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35979 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35981 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35982 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35983 this.thumbEl.hide();
35985 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35986 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35988 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35989 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35990 this.errorEl.hide();
35992 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35993 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35994 this.footerEl.hide();
35996 this.setThumbBoxSize();
36002 this.fireEvent('initial', this);
36009 window.addEventListener("resize", function() { _this.resize(); } );
36011 this.bodyEl.on('click', this.beforeSelectFile, this);
36014 this.bodyEl.on('touchstart', this.onTouchStart, this);
36015 this.bodyEl.on('touchmove', this.onTouchMove, this);
36016 this.bodyEl.on('touchend', this.onTouchEnd, this);
36020 this.bodyEl.on('mousedown', this.onMouseDown, this);
36021 this.bodyEl.on('mousemove', this.onMouseMove, this);
36022 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
36023 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
36024 Roo.get(document).on('mouseup', this.onMouseUp, this);
36027 this.selectorEl.on('change', this.onFileSelected, this);
36033 this.baseScale = 1;
36035 this.baseRotate = 1;
36036 this.dragable = false;
36037 this.pinching = false;
36040 this.cropData = false;
36041 this.notifyEl.dom.innerHTML = this.emptyText;
36043 this.selectorEl.dom.value = '';
36047 resize : function()
36049 if(this.fireEvent('resize', this) != false){
36050 this.setThumbBoxPosition();
36051 this.setCanvasPosition();
36055 onFooterButtonClick : function(e, el, o, type)
36058 case 'rotate-left' :
36059 this.onRotateLeft(e);
36061 case 'rotate-right' :
36062 this.onRotateRight(e);
36065 this.beforeSelectFile(e);
36080 this.fireEvent('footerbuttonclick', this, type);
36083 beforeSelectFile : function(e)
36085 e.preventDefault();
36087 if(this.fireEvent('beforeselectfile', this) != false){
36088 this.selectorEl.dom.click();
36092 onFileSelected : function(e)
36094 e.preventDefault();
36096 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
36100 var file = this.selectorEl.dom.files[0];
36102 if(this.fireEvent('inspect', this, file) != false){
36103 this.prepare(file);
36108 trash : function(e)
36110 this.fireEvent('trash', this);
36113 download : function(e)
36115 this.fireEvent('download', this);
36118 loadCanvas : function(src)
36120 if(this.fireEvent('beforeloadcanvas', this, src) != false){
36124 this.imageEl = document.createElement('img');
36128 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
36130 this.imageEl.src = src;
36134 onLoadCanvas : function()
36136 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
36137 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
36139 this.bodyEl.un('click', this.beforeSelectFile, this);
36141 this.notifyEl.hide();
36142 this.thumbEl.show();
36143 this.footerEl.show();
36145 this.baseRotateLevel();
36147 if(this.isDocument){
36148 this.setThumbBoxSize();
36151 this.setThumbBoxPosition();
36153 this.baseScaleLevel();
36159 this.canvasLoaded = true;
36162 this.maskEl.unmask();
36167 setCanvasPosition : function()
36169 if(!this.canvasEl){
36173 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
36174 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
36176 this.previewEl.setLeft(pw);
36177 this.previewEl.setTop(ph);
36181 onMouseDown : function(e)
36185 this.dragable = true;
36186 this.pinching = false;
36188 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
36189 this.dragable = false;
36193 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36194 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36198 onMouseMove : function(e)
36202 if(!this.canvasLoaded){
36206 if (!this.dragable){
36210 var minX = Math.ceil(this.thumbEl.getLeft(true));
36211 var minY = Math.ceil(this.thumbEl.getTop(true));
36213 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
36214 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
36216 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36217 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36219 x = x - this.mouseX;
36220 y = y - this.mouseY;
36222 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
36223 var bgY = Math.ceil(y + this.previewEl.getTop(true));
36225 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
36226 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
36228 this.previewEl.setLeft(bgX);
36229 this.previewEl.setTop(bgY);
36231 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36232 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36235 onMouseUp : function(e)
36239 this.dragable = false;
36242 onMouseWheel : function(e)
36246 this.startScale = this.scale;
36248 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
36250 if(!this.zoomable()){
36251 this.scale = this.startScale;
36260 zoomable : function()
36262 var minScale = this.thumbEl.getWidth() / this.minWidth;
36264 if(this.minWidth < this.minHeight){
36265 minScale = this.thumbEl.getHeight() / this.minHeight;
36268 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
36269 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
36273 (this.rotate == 0 || this.rotate == 180) &&
36275 width > this.imageEl.OriginWidth ||
36276 height > this.imageEl.OriginHeight ||
36277 (width < this.minWidth && height < this.minHeight)
36285 (this.rotate == 90 || this.rotate == 270) &&
36287 width > this.imageEl.OriginWidth ||
36288 height > this.imageEl.OriginHeight ||
36289 (width < this.minHeight && height < this.minWidth)
36296 !this.isDocument &&
36297 (this.rotate == 0 || this.rotate == 180) &&
36299 width < this.minWidth ||
36300 width > this.imageEl.OriginWidth ||
36301 height < this.minHeight ||
36302 height > this.imageEl.OriginHeight
36309 !this.isDocument &&
36310 (this.rotate == 90 || this.rotate == 270) &&
36312 width < this.minHeight ||
36313 width > this.imageEl.OriginWidth ||
36314 height < this.minWidth ||
36315 height > this.imageEl.OriginHeight
36325 onRotateLeft : function(e)
36327 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
36329 var minScale = this.thumbEl.getWidth() / this.minWidth;
36331 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
36332 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
36334 this.startScale = this.scale;
36336 while (this.getScaleLevel() < minScale){
36338 this.scale = this.scale + 1;
36340 if(!this.zoomable()){
36345 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
36346 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
36351 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
36358 this.scale = this.startScale;
36360 this.onRotateFail();
36365 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
36367 if(this.isDocument){
36368 this.setThumbBoxSize();
36369 this.setThumbBoxPosition();
36370 this.setCanvasPosition();
36375 this.fireEvent('rotate', this, 'left');
36379 onRotateRight : function(e)
36381 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
36383 var minScale = this.thumbEl.getWidth() / this.minWidth;
36385 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
36386 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
36388 this.startScale = this.scale;
36390 while (this.getScaleLevel() < minScale){
36392 this.scale = this.scale + 1;
36394 if(!this.zoomable()){
36399 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
36400 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
36405 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36412 this.scale = this.startScale;
36414 this.onRotateFail();
36419 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36421 if(this.isDocument){
36422 this.setThumbBoxSize();
36423 this.setThumbBoxPosition();
36424 this.setCanvasPosition();
36429 this.fireEvent('rotate', this, 'right');
36432 onRotateFail : function()
36434 this.errorEl.show(true);
36438 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
36443 this.previewEl.dom.innerHTML = '';
36445 var canvasEl = document.createElement("canvas");
36447 var contextEl = canvasEl.getContext("2d");
36449 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36450 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36451 var center = this.imageEl.OriginWidth / 2;
36453 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
36454 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36455 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36456 center = this.imageEl.OriginHeight / 2;
36459 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
36461 contextEl.translate(center, center);
36462 contextEl.rotate(this.rotate * Math.PI / 180);
36464 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36466 this.canvasEl = document.createElement("canvas");
36468 this.contextEl = this.canvasEl.getContext("2d");
36470 switch (this.rotate) {
36473 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36474 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36476 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36481 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36482 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36484 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36485 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);
36489 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36494 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36495 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36497 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36498 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);
36502 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);
36507 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36508 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36510 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36511 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36515 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);
36522 this.previewEl.appendChild(this.canvasEl);
36524 this.setCanvasPosition();
36529 if(!this.canvasLoaded){
36533 var imageCanvas = document.createElement("canvas");
36535 var imageContext = imageCanvas.getContext("2d");
36537 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36538 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36540 var center = imageCanvas.width / 2;
36542 imageContext.translate(center, center);
36544 imageContext.rotate(this.rotate * Math.PI / 180);
36546 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36548 var canvas = document.createElement("canvas");
36550 var context = canvas.getContext("2d");
36552 canvas.width = this.minWidth;
36553 canvas.height = this.minHeight;
36555 switch (this.rotate) {
36558 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36559 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36561 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36562 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36564 var targetWidth = this.minWidth - 2 * x;
36565 var targetHeight = this.minHeight - 2 * y;
36569 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36570 scale = targetWidth / width;
36573 if(x > 0 && y == 0){
36574 scale = targetHeight / height;
36577 if(x > 0 && y > 0){
36578 scale = targetWidth / width;
36580 if(width < height){
36581 scale = targetHeight / height;
36585 context.scale(scale, scale);
36587 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36588 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36590 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36591 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36593 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36598 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36599 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36601 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36602 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36604 var targetWidth = this.minWidth - 2 * x;
36605 var targetHeight = this.minHeight - 2 * y;
36609 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36610 scale = targetWidth / width;
36613 if(x > 0 && y == 0){
36614 scale = targetHeight / height;
36617 if(x > 0 && y > 0){
36618 scale = targetWidth / width;
36620 if(width < height){
36621 scale = targetHeight / height;
36625 context.scale(scale, scale);
36627 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36628 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36630 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36631 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36633 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36635 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36640 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36641 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36643 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36644 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36646 var targetWidth = this.minWidth - 2 * x;
36647 var targetHeight = this.minHeight - 2 * y;
36651 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36652 scale = targetWidth / width;
36655 if(x > 0 && y == 0){
36656 scale = targetHeight / height;
36659 if(x > 0 && y > 0){
36660 scale = targetWidth / width;
36662 if(width < height){
36663 scale = targetHeight / height;
36667 context.scale(scale, scale);
36669 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36670 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36672 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36673 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36675 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36676 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36678 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36683 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36684 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36686 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36687 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36689 var targetWidth = this.minWidth - 2 * x;
36690 var targetHeight = this.minHeight - 2 * y;
36694 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36695 scale = targetWidth / width;
36698 if(x > 0 && y == 0){
36699 scale = targetHeight / height;
36702 if(x > 0 && y > 0){
36703 scale = targetWidth / width;
36705 if(width < height){
36706 scale = targetHeight / height;
36710 context.scale(scale, scale);
36712 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36713 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36715 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36716 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36718 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36720 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36727 this.cropData = canvas.toDataURL(this.cropType);
36729 if(this.fireEvent('crop', this, this.cropData) !== false){
36730 this.process(this.file, this.cropData);
36737 setThumbBoxSize : function()
36741 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
36742 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
36743 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
36745 this.minWidth = width;
36746 this.minHeight = height;
36748 if(this.rotate == 90 || this.rotate == 270){
36749 this.minWidth = height;
36750 this.minHeight = width;
36755 width = Math.ceil(this.minWidth * height / this.minHeight);
36757 if(this.minWidth > this.minHeight){
36759 height = Math.ceil(this.minHeight * width / this.minWidth);
36762 this.thumbEl.setStyle({
36763 width : width + 'px',
36764 height : height + 'px'
36771 setThumbBoxPosition : function()
36773 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
36774 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
36776 this.thumbEl.setLeft(x);
36777 this.thumbEl.setTop(y);
36781 baseRotateLevel : function()
36783 this.baseRotate = 1;
36786 typeof(this.exif) != 'undefined' &&
36787 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
36788 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
36790 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
36793 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
36797 baseScaleLevel : function()
36801 if(this.isDocument){
36803 if(this.baseRotate == 6 || this.baseRotate == 8){
36805 height = this.thumbEl.getHeight();
36806 this.baseScale = height / this.imageEl.OriginWidth;
36808 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
36809 width = this.thumbEl.getWidth();
36810 this.baseScale = width / this.imageEl.OriginHeight;
36816 height = this.thumbEl.getHeight();
36817 this.baseScale = height / this.imageEl.OriginHeight;
36819 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
36820 width = this.thumbEl.getWidth();
36821 this.baseScale = width / this.imageEl.OriginWidth;
36827 if(this.baseRotate == 6 || this.baseRotate == 8){
36829 width = this.thumbEl.getHeight();
36830 this.baseScale = width / this.imageEl.OriginHeight;
36832 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
36833 height = this.thumbEl.getWidth();
36834 this.baseScale = height / this.imageEl.OriginHeight;
36837 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36838 height = this.thumbEl.getWidth();
36839 this.baseScale = height / this.imageEl.OriginHeight;
36841 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
36842 width = this.thumbEl.getHeight();
36843 this.baseScale = width / this.imageEl.OriginWidth;
36850 width = this.thumbEl.getWidth();
36851 this.baseScale = width / this.imageEl.OriginWidth;
36853 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
36854 height = this.thumbEl.getHeight();
36855 this.baseScale = height / this.imageEl.OriginHeight;
36858 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36860 height = this.thumbEl.getHeight();
36861 this.baseScale = height / this.imageEl.OriginHeight;
36863 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
36864 width = this.thumbEl.getWidth();
36865 this.baseScale = width / this.imageEl.OriginWidth;
36873 getScaleLevel : function()
36875 return this.baseScale * Math.pow(1.1, this.scale);
36878 onTouchStart : function(e)
36880 if(!this.canvasLoaded){
36881 this.beforeSelectFile(e);
36885 var touches = e.browserEvent.touches;
36891 if(touches.length == 1){
36892 this.onMouseDown(e);
36896 if(touches.length != 2){
36902 for(var i = 0, finger; finger = touches[i]; i++){
36903 coords.push(finger.pageX, finger.pageY);
36906 var x = Math.pow(coords[0] - coords[2], 2);
36907 var y = Math.pow(coords[1] - coords[3], 2);
36909 this.startDistance = Math.sqrt(x + y);
36911 this.startScale = this.scale;
36913 this.pinching = true;
36914 this.dragable = false;
36918 onTouchMove : function(e)
36920 if(!this.pinching && !this.dragable){
36924 var touches = e.browserEvent.touches;
36931 this.onMouseMove(e);
36937 for(var i = 0, finger; finger = touches[i]; i++){
36938 coords.push(finger.pageX, finger.pageY);
36941 var x = Math.pow(coords[0] - coords[2], 2);
36942 var y = Math.pow(coords[1] - coords[3], 2);
36944 this.endDistance = Math.sqrt(x + y);
36946 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36948 if(!this.zoomable()){
36949 this.scale = this.startScale;
36957 onTouchEnd : function(e)
36959 this.pinching = false;
36960 this.dragable = false;
36964 process : function(file, crop)
36967 this.maskEl.mask(this.loadingText);
36970 this.xhr = new XMLHttpRequest();
36972 file.xhr = this.xhr;
36974 this.xhr.open(this.method, this.url, true);
36977 "Accept": "application/json",
36978 "Cache-Control": "no-cache",
36979 "X-Requested-With": "XMLHttpRequest"
36982 for (var headerName in headers) {
36983 var headerValue = headers[headerName];
36985 this.xhr.setRequestHeader(headerName, headerValue);
36991 this.xhr.onload = function()
36993 _this.xhrOnLoad(_this.xhr);
36996 this.xhr.onerror = function()
36998 _this.xhrOnError(_this.xhr);
37001 var formData = new FormData();
37003 formData.append('returnHTML', 'NO');
37006 formData.append('crop', crop);
37009 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
37010 formData.append(this.paramName, file, file.name);
37013 if(typeof(file.filename) != 'undefined'){
37014 formData.append('filename', file.filename);
37017 if(typeof(file.mimetype) != 'undefined'){
37018 formData.append('mimetype', file.mimetype);
37021 if(this.fireEvent('arrange', this, formData) != false){
37022 this.xhr.send(formData);
37026 xhrOnLoad : function(xhr)
37029 this.maskEl.unmask();
37032 if (xhr.readyState !== 4) {
37033 this.fireEvent('exception', this, xhr);
37037 var response = Roo.decode(xhr.responseText);
37039 if(!response.success){
37040 this.fireEvent('exception', this, xhr);
37044 var response = Roo.decode(xhr.responseText);
37046 this.fireEvent('upload', this, response);
37050 xhrOnError : function()
37053 this.maskEl.unmask();
37056 Roo.log('xhr on error');
37058 var response = Roo.decode(xhr.responseText);
37064 prepare : function(file)
37067 this.maskEl.mask(this.loadingText);
37073 if(typeof(file) === 'string'){
37074 this.loadCanvas(file);
37078 if(!file || !this.urlAPI){
37083 this.cropType = file.type;
37087 if(this.fireEvent('prepare', this, this.file) != false){
37089 var reader = new FileReader();
37091 reader.onload = function (e) {
37092 if (e.target.error) {
37093 Roo.log(e.target.error);
37097 var buffer = e.target.result,
37098 dataView = new DataView(buffer),
37100 maxOffset = dataView.byteLength - 4,
37104 if (dataView.getUint16(0) === 0xffd8) {
37105 while (offset < maxOffset) {
37106 markerBytes = dataView.getUint16(offset);
37108 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
37109 markerLength = dataView.getUint16(offset + 2) + 2;
37110 if (offset + markerLength > dataView.byteLength) {
37111 Roo.log('Invalid meta data: Invalid segment size.');
37115 if(markerBytes == 0xffe1){
37116 _this.parseExifData(
37123 offset += markerLength;
37133 var url = _this.urlAPI.createObjectURL(_this.file);
37135 _this.loadCanvas(url);
37140 reader.readAsArrayBuffer(this.file);
37146 parseExifData : function(dataView, offset, length)
37148 var tiffOffset = offset + 10,
37152 if (dataView.getUint32(offset + 4) !== 0x45786966) {
37153 // No Exif data, might be XMP data instead
37157 // Check for the ASCII code for "Exif" (0x45786966):
37158 if (dataView.getUint32(offset + 4) !== 0x45786966) {
37159 // No Exif data, might be XMP data instead
37162 if (tiffOffset + 8 > dataView.byteLength) {
37163 Roo.log('Invalid Exif data: Invalid segment size.');
37166 // Check for the two null bytes:
37167 if (dataView.getUint16(offset + 8) !== 0x0000) {
37168 Roo.log('Invalid Exif data: Missing byte alignment offset.');
37171 // Check the byte alignment:
37172 switch (dataView.getUint16(tiffOffset)) {
37174 littleEndian = true;
37177 littleEndian = false;
37180 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
37183 // Check for the TIFF tag marker (0x002A):
37184 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
37185 Roo.log('Invalid Exif data: Missing TIFF marker.');
37188 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
37189 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
37191 this.parseExifTags(
37194 tiffOffset + dirOffset,
37199 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
37204 if (dirOffset + 6 > dataView.byteLength) {
37205 Roo.log('Invalid Exif data: Invalid directory offset.');
37208 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
37209 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
37210 if (dirEndOffset + 4 > dataView.byteLength) {
37211 Roo.log('Invalid Exif data: Invalid directory size.');
37214 for (i = 0; i < tagsNumber; i += 1) {
37218 dirOffset + 2 + 12 * i, // tag offset
37222 // Return the offset to the next directory:
37223 return dataView.getUint32(dirEndOffset, littleEndian);
37226 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
37228 var tag = dataView.getUint16(offset, littleEndian);
37230 this.exif[tag] = this.getExifValue(
37234 dataView.getUint16(offset + 2, littleEndian), // tag type
37235 dataView.getUint32(offset + 4, littleEndian), // tag length
37240 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
37242 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
37251 Roo.log('Invalid Exif data: Invalid tag type.');
37255 tagSize = tagType.size * length;
37256 // Determine if the value is contained in the dataOffset bytes,
37257 // or if the value at the dataOffset is a pointer to the actual data:
37258 dataOffset = tagSize > 4 ?
37259 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
37260 if (dataOffset + tagSize > dataView.byteLength) {
37261 Roo.log('Invalid Exif data: Invalid data offset.');
37264 if (length === 1) {
37265 return tagType.getValue(dataView, dataOffset, littleEndian);
37268 for (i = 0; i < length; i += 1) {
37269 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
37272 if (tagType.ascii) {
37274 // Concatenate the chars:
37275 for (i = 0; i < values.length; i += 1) {
37277 // Ignore the terminating NULL byte(s):
37278 if (c === '\u0000') {
37290 Roo.apply(Roo.bootstrap.UploadCropbox, {
37292 'Orientation': 0x0112
37296 1: 0, //'top-left',
37298 3: 180, //'bottom-right',
37299 // 4: 'bottom-left',
37301 6: 90, //'right-top',
37302 // 7: 'right-bottom',
37303 8: 270 //'left-bottom'
37307 // byte, 8-bit unsigned int:
37309 getValue: function (dataView, dataOffset) {
37310 return dataView.getUint8(dataOffset);
37314 // ascii, 8-bit byte:
37316 getValue: function (dataView, dataOffset) {
37317 return String.fromCharCode(dataView.getUint8(dataOffset));
37322 // short, 16 bit int:
37324 getValue: function (dataView, dataOffset, littleEndian) {
37325 return dataView.getUint16(dataOffset, littleEndian);
37329 // long, 32 bit int:
37331 getValue: function (dataView, dataOffset, littleEndian) {
37332 return dataView.getUint32(dataOffset, littleEndian);
37336 // rational = two long values, first is numerator, second is denominator:
37338 getValue: function (dataView, dataOffset, littleEndian) {
37339 return dataView.getUint32(dataOffset, littleEndian) /
37340 dataView.getUint32(dataOffset + 4, littleEndian);
37344 // slong, 32 bit signed int:
37346 getValue: function (dataView, dataOffset, littleEndian) {
37347 return dataView.getInt32(dataOffset, littleEndian);
37351 // srational, two slongs, first is numerator, second is denominator:
37353 getValue: function (dataView, dataOffset, littleEndian) {
37354 return dataView.getInt32(dataOffset, littleEndian) /
37355 dataView.getInt32(dataOffset + 4, littleEndian);
37365 cls : 'btn-group roo-upload-cropbox-rotate-left',
37366 action : 'rotate-left',
37370 cls : 'btn btn-default',
37371 html : '<i class="fa fa-undo"></i>'
37377 cls : 'btn-group roo-upload-cropbox-picture',
37378 action : 'picture',
37382 cls : 'btn btn-default',
37383 html : '<i class="fa fa-picture-o"></i>'
37389 cls : 'btn-group roo-upload-cropbox-rotate-right',
37390 action : 'rotate-right',
37394 cls : 'btn btn-default',
37395 html : '<i class="fa fa-repeat"></i>'
37403 cls : 'btn-group roo-upload-cropbox-rotate-left',
37404 action : 'rotate-left',
37408 cls : 'btn btn-default',
37409 html : '<i class="fa fa-undo"></i>'
37415 cls : 'btn-group roo-upload-cropbox-download',
37416 action : 'download',
37420 cls : 'btn btn-default',
37421 html : '<i class="fa fa-download"></i>'
37427 cls : 'btn-group roo-upload-cropbox-crop',
37432 cls : 'btn btn-default',
37433 html : '<i class="fa fa-crop"></i>'
37439 cls : 'btn-group roo-upload-cropbox-trash',
37444 cls : 'btn btn-default',
37445 html : '<i class="fa fa-trash"></i>'
37451 cls : 'btn-group roo-upload-cropbox-rotate-right',
37452 action : 'rotate-right',
37456 cls : 'btn btn-default',
37457 html : '<i class="fa fa-repeat"></i>'
37465 cls : 'btn-group roo-upload-cropbox-rotate-left',
37466 action : 'rotate-left',
37470 cls : 'btn btn-default',
37471 html : '<i class="fa fa-undo"></i>'
37477 cls : 'btn-group roo-upload-cropbox-rotate-right',
37478 action : 'rotate-right',
37482 cls : 'btn btn-default',
37483 html : '<i class="fa fa-repeat"></i>'
37496 * @class Roo.bootstrap.DocumentManager
37497 * @extends Roo.bootstrap.Component
37498 * Bootstrap DocumentManager class
37499 * @cfg {String} paramName default 'imageUpload'
37500 * @cfg {String} toolTipName default 'filename'
37501 * @cfg {String} method default POST
37502 * @cfg {String} url action url
37503 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
37504 * @cfg {Boolean} multiple multiple upload default true
37505 * @cfg {Number} thumbSize default 300
37506 * @cfg {String} fieldLabel
37507 * @cfg {Number} labelWidth default 4
37508 * @cfg {String} labelAlign (left|top) default left
37509 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
37510 * @cfg {Number} labellg set the width of label (1-12)
37511 * @cfg {Number} labelmd set the width of label (1-12)
37512 * @cfg {Number} labelsm set the width of label (1-12)
37513 * @cfg {Number} labelxs set the width of label (1-12)
37516 * Create a new DocumentManager
37517 * @param {Object} config The config object
37520 Roo.bootstrap.DocumentManager = function(config){
37521 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
37524 this.delegates = [];
37529 * Fire when initial the DocumentManager
37530 * @param {Roo.bootstrap.DocumentManager} this
37535 * inspect selected file
37536 * @param {Roo.bootstrap.DocumentManager} this
37537 * @param {File} file
37542 * Fire when xhr load exception
37543 * @param {Roo.bootstrap.DocumentManager} this
37544 * @param {XMLHttpRequest} xhr
37546 "exception" : true,
37548 * @event afterupload
37549 * Fire when xhr load exception
37550 * @param {Roo.bootstrap.DocumentManager} this
37551 * @param {XMLHttpRequest} xhr
37553 "afterupload" : true,
37556 * prepare the form data
37557 * @param {Roo.bootstrap.DocumentManager} this
37558 * @param {Object} formData
37563 * Fire when remove the file
37564 * @param {Roo.bootstrap.DocumentManager} this
37565 * @param {Object} file
37570 * Fire after refresh the file
37571 * @param {Roo.bootstrap.DocumentManager} this
37576 * Fire after click the image
37577 * @param {Roo.bootstrap.DocumentManager} this
37578 * @param {Object} file
37583 * Fire when upload a image and editable set to true
37584 * @param {Roo.bootstrap.DocumentManager} this
37585 * @param {Object} file
37589 * @event beforeselectfile
37590 * Fire before select file
37591 * @param {Roo.bootstrap.DocumentManager} this
37593 "beforeselectfile" : true,
37596 * Fire before process file
37597 * @param {Roo.bootstrap.DocumentManager} this
37598 * @param {Object} file
37602 * @event previewrendered
37603 * Fire when preview rendered
37604 * @param {Roo.bootstrap.DocumentManager} this
37605 * @param {Object} file
37607 "previewrendered" : true,
37610 "previewResize" : true
37615 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
37624 paramName : 'imageUpload',
37625 toolTipName : 'filename',
37628 labelAlign : 'left',
37638 getAutoCreate : function()
37640 var managerWidget = {
37642 cls : 'roo-document-manager',
37646 cls : 'roo-document-manager-selector',
37651 cls : 'roo-document-manager-uploader',
37655 cls : 'roo-document-manager-upload-btn',
37656 html : '<i class="fa fa-plus"></i>'
37667 cls : 'column col-md-12',
37672 if(this.fieldLabel.length){
37677 cls : 'column col-md-12',
37678 html : this.fieldLabel
37682 cls : 'column col-md-12',
37687 if(this.labelAlign == 'left'){
37692 html : this.fieldLabel
37701 if(this.labelWidth > 12){
37702 content[0].style = "width: " + this.labelWidth + 'px';
37705 if(this.labelWidth < 13 && this.labelmd == 0){
37706 this.labelmd = this.labelWidth;
37709 if(this.labellg > 0){
37710 content[0].cls += ' col-lg-' + this.labellg;
37711 content[1].cls += ' col-lg-' + (12 - this.labellg);
37714 if(this.labelmd > 0){
37715 content[0].cls += ' col-md-' + this.labelmd;
37716 content[1].cls += ' col-md-' + (12 - this.labelmd);
37719 if(this.labelsm > 0){
37720 content[0].cls += ' col-sm-' + this.labelsm;
37721 content[1].cls += ' col-sm-' + (12 - this.labelsm);
37724 if(this.labelxs > 0){
37725 content[0].cls += ' col-xs-' + this.labelxs;
37726 content[1].cls += ' col-xs-' + (12 - this.labelxs);
37734 cls : 'row clearfix',
37742 initEvents : function()
37744 this.managerEl = this.el.select('.roo-document-manager', true).first();
37745 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37747 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
37748 this.selectorEl.hide();
37751 this.selectorEl.attr('multiple', 'multiple');
37754 this.selectorEl.on('change', this.onFileSelected, this);
37756 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
37757 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37759 this.uploader.on('click', this.onUploaderClick, this);
37761 this.renderProgressDialog();
37765 window.addEventListener("resize", function() { _this.refresh(); } );
37767 this.fireEvent('initial', this);
37770 renderProgressDialog : function()
37774 this.progressDialog = new Roo.bootstrap.Modal({
37775 cls : 'roo-document-manager-progress-dialog',
37776 allow_close : false,
37787 btnclick : function() {
37788 _this.uploadCancel();
37794 this.progressDialog.render(Roo.get(document.body));
37796 this.progress = new Roo.bootstrap.Progress({
37797 cls : 'roo-document-manager-progress',
37802 this.progress.render(this.progressDialog.getChildContainer());
37804 this.progressBar = new Roo.bootstrap.ProgressBar({
37805 cls : 'roo-document-manager-progress-bar',
37808 aria_valuemax : 12,
37812 this.progressBar.render(this.progress.getChildContainer());
37815 onUploaderClick : function(e)
37817 e.preventDefault();
37819 if(this.fireEvent('beforeselectfile', this) != false){
37820 this.selectorEl.dom.click();
37825 onFileSelected : function(e)
37827 e.preventDefault();
37829 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
37833 Roo.each(this.selectorEl.dom.files, function(file){
37834 if(this.fireEvent('inspect', this, file) != false){
37835 this.files.push(file);
37845 this.selectorEl.dom.value = '';
37847 if(!this.files || !this.files.length){
37851 if(this.boxes > 0 && this.files.length > this.boxes){
37852 this.files = this.files.slice(0, this.boxes);
37855 this.uploader.show();
37857 if(this.boxes > 0 && this.files.length > this.boxes - 1){
37858 this.uploader.hide();
37867 Roo.each(this.files, function(file){
37869 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37870 var f = this.renderPreview(file);
37875 if(file.type.indexOf('image') != -1){
37876 this.delegates.push(
37878 _this.process(file);
37879 }).createDelegate(this)
37887 _this.process(file);
37888 }).createDelegate(this)
37893 this.files = files;
37895 this.delegates = this.delegates.concat(docs);
37897 if(!this.delegates.length){
37902 this.progressBar.aria_valuemax = this.delegates.length;
37909 arrange : function()
37911 if(!this.delegates.length){
37912 this.progressDialog.hide();
37917 var delegate = this.delegates.shift();
37919 this.progressDialog.show();
37921 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
37923 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
37928 refresh : function()
37930 this.uploader.show();
37932 if(this.boxes > 0 && this.files.length > this.boxes - 1){
37933 this.uploader.hide();
37936 Roo.isTouch ? this.closable(false) : this.closable(true);
37938 this.fireEvent('refresh', this);
37941 onRemove : function(e, el, o)
37943 e.preventDefault();
37945 this.fireEvent('remove', this, o);
37949 remove : function(o)
37953 Roo.each(this.files, function(file){
37954 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37963 this.files = files;
37970 Roo.each(this.files, function(file){
37975 file.target.remove();
37984 onClick : function(e, el, o)
37986 e.preventDefault();
37988 this.fireEvent('click', this, o);
37992 closable : function(closable)
37994 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37996 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
38008 xhrOnLoad : function(xhr)
38010 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
38014 if (xhr.readyState !== 4) {
38016 this.fireEvent('exception', this, xhr);
38020 var response = Roo.decode(xhr.responseText);
38022 if(!response.success){
38024 this.fireEvent('exception', this, xhr);
38028 var file = this.renderPreview(response.data);
38030 this.files.push(file);
38034 this.fireEvent('afterupload', this, xhr);
38038 xhrOnError : function(xhr)
38040 Roo.log('xhr on error');
38042 var response = Roo.decode(xhr.responseText);
38049 process : function(file)
38051 if(this.fireEvent('process', this, file) !== false){
38052 if(this.editable && file.type.indexOf('image') != -1){
38053 this.fireEvent('edit', this, file);
38057 this.uploadStart(file, false);
38064 uploadStart : function(file, crop)
38066 this.xhr = new XMLHttpRequest();
38068 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
38073 file.xhr = this.xhr;
38075 this.managerEl.createChild({
38077 cls : 'roo-document-manager-loading',
38081 tooltip : file.name,
38082 cls : 'roo-document-manager-thumb',
38083 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
38089 this.xhr.open(this.method, this.url, true);
38092 "Accept": "application/json",
38093 "Cache-Control": "no-cache",
38094 "X-Requested-With": "XMLHttpRequest"
38097 for (var headerName in headers) {
38098 var headerValue = headers[headerName];
38100 this.xhr.setRequestHeader(headerName, headerValue);
38106 this.xhr.onload = function()
38108 _this.xhrOnLoad(_this.xhr);
38111 this.xhr.onerror = function()
38113 _this.xhrOnError(_this.xhr);
38116 var formData = new FormData();
38118 formData.append('returnHTML', 'NO');
38121 formData.append('crop', crop);
38124 formData.append(this.paramName, file, file.name);
38131 if(this.fireEvent('prepare', this, formData, options) != false){
38133 if(options.manually){
38137 this.xhr.send(formData);
38141 this.uploadCancel();
38144 uploadCancel : function()
38150 this.delegates = [];
38152 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
38159 renderPreview : function(file)
38161 if(typeof(file.target) != 'undefined' && file.target){
38165 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
38167 var previewEl = this.managerEl.createChild({
38169 cls : 'roo-document-manager-preview',
38173 tooltip : file[this.toolTipName],
38174 cls : 'roo-document-manager-thumb',
38175 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
38180 html : '<i class="fa fa-times-circle"></i>'
38185 var close = previewEl.select('button.close', true).first();
38187 close.on('click', this.onRemove, this, file);
38189 file.target = previewEl;
38191 var image = previewEl.select('img', true).first();
38195 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
38197 image.on('click', this.onClick, this, file);
38199 this.fireEvent('previewrendered', this, file);
38205 onPreviewLoad : function(file, image)
38207 if(typeof(file.target) == 'undefined' || !file.target){
38211 var width = image.dom.naturalWidth || image.dom.width;
38212 var height = image.dom.naturalHeight || image.dom.height;
38214 if(!this.previewResize) {
38218 if(width > height){
38219 file.target.addClass('wide');
38223 file.target.addClass('tall');
38228 uploadFromSource : function(file, crop)
38230 this.xhr = new XMLHttpRequest();
38232 this.managerEl.createChild({
38234 cls : 'roo-document-manager-loading',
38238 tooltip : file.name,
38239 cls : 'roo-document-manager-thumb',
38240 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
38246 this.xhr.open(this.method, this.url, true);
38249 "Accept": "application/json",
38250 "Cache-Control": "no-cache",
38251 "X-Requested-With": "XMLHttpRequest"
38254 for (var headerName in headers) {
38255 var headerValue = headers[headerName];
38257 this.xhr.setRequestHeader(headerName, headerValue);
38263 this.xhr.onload = function()
38265 _this.xhrOnLoad(_this.xhr);
38268 this.xhr.onerror = function()
38270 _this.xhrOnError(_this.xhr);
38273 var formData = new FormData();
38275 formData.append('returnHTML', 'NO');
38277 formData.append('crop', crop);
38279 if(typeof(file.filename) != 'undefined'){
38280 formData.append('filename', file.filename);
38283 if(typeof(file.mimetype) != 'undefined'){
38284 formData.append('mimetype', file.mimetype);
38289 if(this.fireEvent('prepare', this, formData) != false){
38290 this.xhr.send(formData);
38300 * @class Roo.bootstrap.DocumentViewer
38301 * @extends Roo.bootstrap.Component
38302 * Bootstrap DocumentViewer class
38303 * @cfg {Boolean} showDownload (true|false) show download button (default true)
38304 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
38307 * Create a new DocumentViewer
38308 * @param {Object} config The config object
38311 Roo.bootstrap.DocumentViewer = function(config){
38312 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
38317 * Fire after initEvent
38318 * @param {Roo.bootstrap.DocumentViewer} this
38324 * @param {Roo.bootstrap.DocumentViewer} this
38329 * Fire after download button
38330 * @param {Roo.bootstrap.DocumentViewer} this
38335 * Fire after trash button
38336 * @param {Roo.bootstrap.DocumentViewer} this
38343 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
38345 showDownload : true,
38349 getAutoCreate : function()
38353 cls : 'roo-document-viewer',
38357 cls : 'roo-document-viewer-body',
38361 cls : 'roo-document-viewer-thumb',
38365 cls : 'roo-document-viewer-image'
38373 cls : 'roo-document-viewer-footer',
38376 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
38380 cls : 'btn-group roo-document-viewer-download',
38384 cls : 'btn btn-default',
38385 html : '<i class="fa fa-download"></i>'
38391 cls : 'btn-group roo-document-viewer-trash',
38395 cls : 'btn btn-default',
38396 html : '<i class="fa fa-trash"></i>'
38409 initEvents : function()
38411 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
38412 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
38414 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
38415 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
38417 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
38418 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
38420 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
38421 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
38423 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
38424 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
38426 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
38427 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
38429 this.bodyEl.on('click', this.onClick, this);
38430 this.downloadBtn.on('click', this.onDownload, this);
38431 this.trashBtn.on('click', this.onTrash, this);
38433 this.downloadBtn.hide();
38434 this.trashBtn.hide();
38436 if(this.showDownload){
38437 this.downloadBtn.show();
38440 if(this.showTrash){
38441 this.trashBtn.show();
38444 if(!this.showDownload && !this.showTrash) {
38445 this.footerEl.hide();
38450 initial : function()
38452 this.fireEvent('initial', this);
38456 onClick : function(e)
38458 e.preventDefault();
38460 this.fireEvent('click', this);
38463 onDownload : function(e)
38465 e.preventDefault();
38467 this.fireEvent('download', this);
38470 onTrash : function(e)
38472 e.preventDefault();
38474 this.fireEvent('trash', this);
38486 * @class Roo.bootstrap.form.FieldLabel
38487 * @extends Roo.bootstrap.Component
38488 * Bootstrap FieldLabel class
38489 * @cfg {String} html contents of the element
38490 * @cfg {String} tag tag of the element default label
38491 * @cfg {String} cls class of the element
38492 * @cfg {String} target label target
38493 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
38494 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
38495 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
38496 * @cfg {String} iconTooltip default "This field is required"
38497 * @cfg {String} indicatorpos (left|right) default left
38500 * Create a new FieldLabel
38501 * @param {Object} config The config object
38504 Roo.bootstrap.form.FieldLabel = function(config){
38505 Roo.bootstrap.Element.superclass.constructor.call(this, config);
38510 * Fires after the field has been marked as invalid.
38511 * @param {Roo.form.FieldLabel} this
38512 * @param {String} msg The validation message
38517 * Fires after the field has been validated with no errors.
38518 * @param {Roo.form.FieldLabel} this
38524 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component, {
38531 invalidClass : 'has-warning',
38532 validClass : 'has-success',
38533 iconTooltip : 'This field is required',
38534 indicatorpos : 'left',
38536 getAutoCreate : function(){
38539 if (!this.allowBlank) {
38545 cls : 'roo-bootstrap-field-label ' + this.cls,
38550 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
38551 tooltip : this.iconTooltip
38560 if(this.indicatorpos == 'right'){
38563 cls : 'roo-bootstrap-field-label ' + this.cls,
38572 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
38573 tooltip : this.iconTooltip
38582 initEvents: function()
38584 Roo.bootstrap.Element.superclass.initEvents.call(this);
38586 this.indicator = this.indicatorEl();
38588 if(this.indicator){
38589 this.indicator.removeClass('visible');
38590 this.indicator.addClass('invisible');
38593 Roo.bootstrap.form.FieldLabel.register(this);
38596 indicatorEl : function()
38598 var indicator = this.el.select('i.roo-required-indicator',true).first();
38609 * Mark this field as valid
38611 markValid : function()
38613 if(this.indicator){
38614 this.indicator.removeClass('visible');
38615 this.indicator.addClass('invisible');
38617 if (Roo.bootstrap.version == 3) {
38618 this.el.removeClass(this.invalidClass);
38619 this.el.addClass(this.validClass);
38621 this.el.removeClass('is-invalid');
38622 this.el.addClass('is-valid');
38626 this.fireEvent('valid', this);
38630 * Mark this field as invalid
38631 * @param {String} msg The validation message
38633 markInvalid : function(msg)
38635 if(this.indicator){
38636 this.indicator.removeClass('invisible');
38637 this.indicator.addClass('visible');
38639 if (Roo.bootstrap.version == 3) {
38640 this.el.removeClass(this.validClass);
38641 this.el.addClass(this.invalidClass);
38643 this.el.removeClass('is-valid');
38644 this.el.addClass('is-invalid');
38648 this.fireEvent('invalid', this, msg);
38654 Roo.apply(Roo.bootstrap.form.FieldLabel, {
38659 * register a FieldLabel Group
38660 * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
38662 register : function(label)
38664 if(this.groups.hasOwnProperty(label.target)){
38668 this.groups[label.target] = label;
38672 * fetch a FieldLabel Group based on the target
38673 * @param {string} target
38674 * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
38676 get: function(target) {
38677 if (typeof(this.groups[target]) == 'undefined') {
38681 return this.groups[target] ;
38690 * page DateSplitField.
38696 * @class Roo.bootstrap.form.DateSplitField
38697 * @extends Roo.bootstrap.Component
38698 * Bootstrap DateSplitField class
38699 * @cfg {string} fieldLabel - the label associated
38700 * @cfg {Number} labelWidth set the width of label (0-12)
38701 * @cfg {String} labelAlign (top|left)
38702 * @cfg {Boolean} dayAllowBlank (true|false) default false
38703 * @cfg {Boolean} monthAllowBlank (true|false) default false
38704 * @cfg {Boolean} yearAllowBlank (true|false) default false
38705 * @cfg {string} dayPlaceholder
38706 * @cfg {string} monthPlaceholder
38707 * @cfg {string} yearPlaceholder
38708 * @cfg {string} dayFormat default 'd'
38709 * @cfg {string} monthFormat default 'm'
38710 * @cfg {string} yearFormat default 'Y'
38711 * @cfg {Number} labellg set the width of label (1-12)
38712 * @cfg {Number} labelmd set the width of label (1-12)
38713 * @cfg {Number} labelsm set the width of label (1-12)
38714 * @cfg {Number} labelxs set the width of label (1-12)
38718 * Create a new DateSplitField
38719 * @param {Object} config The config object
38722 Roo.bootstrap.form.DateSplitField = function(config){
38723 Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
38729 * getting the data of years
38730 * @param {Roo.bootstrap.form.DateSplitField} this
38731 * @param {Object} years
38736 * getting the data of days
38737 * @param {Roo.bootstrap.form.DateSplitField} this
38738 * @param {Object} days
38743 * Fires after the field has been marked as invalid.
38744 * @param {Roo.form.Field} this
38745 * @param {String} msg The validation message
38750 * Fires after the field has been validated with no errors.
38751 * @param {Roo.form.Field} this
38757 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component, {
38760 labelAlign : 'top',
38762 dayAllowBlank : false,
38763 monthAllowBlank : false,
38764 yearAllowBlank : false,
38765 dayPlaceholder : '',
38766 monthPlaceholder : '',
38767 yearPlaceholder : '',
38771 isFormField : true,
38777 getAutoCreate : function()
38781 cls : 'row roo-date-split-field-group',
38786 cls : 'form-hidden-field roo-date-split-field-group-value',
38792 var labelCls = 'col-md-12';
38793 var contentCls = 'col-md-4';
38795 if(this.fieldLabel){
38799 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
38803 html : this.fieldLabel
38808 if(this.labelAlign == 'left'){
38810 if(this.labelWidth > 12){
38811 label.style = "width: " + this.labelWidth + 'px';
38814 if(this.labelWidth < 13 && this.labelmd == 0){
38815 this.labelmd = this.labelWidth;
38818 if(this.labellg > 0){
38819 labelCls = ' col-lg-' + this.labellg;
38820 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
38823 if(this.labelmd > 0){
38824 labelCls = ' col-md-' + this.labelmd;
38825 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
38828 if(this.labelsm > 0){
38829 labelCls = ' col-sm-' + this.labelsm;
38830 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
38833 if(this.labelxs > 0){
38834 labelCls = ' col-xs-' + this.labelxs;
38835 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
38839 label.cls += ' ' + labelCls;
38841 cfg.cn.push(label);
38844 Roo.each(['day', 'month', 'year'], function(t){
38847 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
38854 inputEl: function ()
38856 return this.el.select('.roo-date-split-field-group-value', true).first();
38859 onRender : function(ct, position)
38863 Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
38865 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
38867 this.dayField = new Roo.bootstrap.form.ComboBox({
38868 allowBlank : this.dayAllowBlank,
38869 alwaysQuery : true,
38870 displayField : 'value',
38873 forceSelection : true,
38875 placeholder : this.dayPlaceholder,
38876 selectOnFocus : true,
38877 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38878 triggerAction : 'all',
38880 valueField : 'value',
38881 store : new Roo.data.SimpleStore({
38882 data : (function() {
38884 _this.fireEvent('days', _this, days);
38887 fields : [ 'value' ]
38890 select : function (_self, record, index)
38892 _this.setValue(_this.getValue());
38897 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
38899 this.monthField = new Roo.bootstrap.form.MonthField({
38900 after : '<i class=\"fa fa-calendar\"></i>',
38901 allowBlank : this.monthAllowBlank,
38902 placeholder : this.monthPlaceholder,
38905 render : function (_self)
38907 this.el.select('span.input-group-addon', true).first().on('click', function(e){
38908 e.preventDefault();
38912 select : function (_self, oldvalue, newvalue)
38914 _this.setValue(_this.getValue());
38919 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
38921 this.yearField = new Roo.bootstrap.form.ComboBox({
38922 allowBlank : this.yearAllowBlank,
38923 alwaysQuery : true,
38924 displayField : 'value',
38927 forceSelection : true,
38929 placeholder : this.yearPlaceholder,
38930 selectOnFocus : true,
38931 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38932 triggerAction : 'all',
38934 valueField : 'value',
38935 store : new Roo.data.SimpleStore({
38936 data : (function() {
38938 _this.fireEvent('years', _this, years);
38941 fields : [ 'value' ]
38944 select : function (_self, record, index)
38946 _this.setValue(_this.getValue());
38951 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38954 setValue : function(v, format)
38956 this.inputEl.dom.value = v;
38958 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38960 var d = Date.parseDate(v, f);
38967 this.setDay(d.format(this.dayFormat));
38968 this.setMonth(d.format(this.monthFormat));
38969 this.setYear(d.format(this.yearFormat));
38976 setDay : function(v)
38978 this.dayField.setValue(v);
38979 this.inputEl.dom.value = this.getValue();
38984 setMonth : function(v)
38986 this.monthField.setValue(v, true);
38987 this.inputEl.dom.value = this.getValue();
38992 setYear : function(v)
38994 this.yearField.setValue(v);
38995 this.inputEl.dom.value = this.getValue();
39000 getDay : function()
39002 return this.dayField.getValue();
39005 getMonth : function()
39007 return this.monthField.getValue();
39010 getYear : function()
39012 return this.yearField.getValue();
39015 getValue : function()
39017 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
39019 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
39029 this.inputEl.dom.value = '';
39034 validate : function()
39036 var d = this.dayField.validate();
39037 var m = this.monthField.validate();
39038 var y = this.yearField.validate();
39043 (!this.dayAllowBlank && !d) ||
39044 (!this.monthAllowBlank && !m) ||
39045 (!this.yearAllowBlank && !y)
39050 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
39059 this.markInvalid();
39064 markValid : function()
39067 var label = this.el.select('label', true).first();
39068 var icon = this.el.select('i.fa-star', true).first();
39074 this.fireEvent('valid', this);
39078 * Mark this field as invalid
39079 * @param {String} msg The validation message
39081 markInvalid : function(msg)
39084 var label = this.el.select('label', true).first();
39085 var icon = this.el.select('i.fa-star', true).first();
39087 if(label && !icon){
39088 this.el.select('.roo-date-split-field-label', true).createChild({
39090 cls : 'text-danger fa fa-lg fa-star',
39091 tooltip : 'This field is required',
39092 style : 'margin-right:5px;'
39096 this.fireEvent('invalid', this, msg);
39099 clearInvalid : function()
39101 var label = this.el.select('label', true).first();
39102 var icon = this.el.select('i.fa-star', true).first();
39108 this.fireEvent('valid', this);
39111 getName: function()
39121 * @class Roo.bootstrap.LayoutMasonry
39122 * @extends Roo.bootstrap.Component
39123 * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
39124 * Bootstrap Layout Masonry class
39127 * http://masonry.desandro.com
39129 * The idea is to render all the bricks based on vertical width...
39131 * The original code extends 'outlayer' - we might need to use that....
39134 * Create a new Element
39135 * @param {Object} config The config object
39138 Roo.bootstrap.LayoutMasonry = function(config){
39140 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
39144 Roo.bootstrap.LayoutMasonry.register(this);
39150 * Fire after layout the items
39151 * @param {Roo.bootstrap.LayoutMasonry} this
39152 * @param {Roo.EventObject} e
39159 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
39162 * @cfg {Boolean} isLayoutInstant = no animation?
39164 isLayoutInstant : false, // needed?
39167 * @cfg {Number} boxWidth width of the columns
39172 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
39177 * @cfg {Number} padWidth padding below box..
39182 * @cfg {Number} gutter gutter width..
39187 * @cfg {Number} maxCols maximum number of columns
39193 * @cfg {Boolean} isAutoInitial defalut true
39195 isAutoInitial : true,
39200 * @cfg {Boolean} isHorizontal defalut false
39202 isHorizontal : false,
39204 currentSize : null,
39210 bricks: null, //CompositeElement
39214 _isLayoutInited : false,
39216 // isAlternative : false, // only use for vertical layout...
39219 * @cfg {Number} alternativePadWidth padding below box..
39221 alternativePadWidth : 50,
39223 selectedBrick : [],
39225 getAutoCreate : function(){
39227 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
39231 cls: 'blog-masonary-wrapper ' + this.cls,
39233 cls : 'mas-boxes masonary'
39240 getChildContainer: function( )
39242 if (this.boxesEl) {
39243 return this.boxesEl;
39246 this.boxesEl = this.el.select('.mas-boxes').first();
39248 return this.boxesEl;
39252 initEvents : function()
39256 if(this.isAutoInitial){
39257 Roo.log('hook children rendered');
39258 this.on('childrenrendered', function() {
39259 Roo.log('children rendered');
39265 initial : function()
39267 this.selectedBrick = [];
39269 this.currentSize = this.el.getBox(true);
39271 Roo.EventManager.onWindowResize(this.resize, this);
39273 if(!this.isAutoInitial){
39281 //this.layout.defer(500,this);
39285 resize : function()
39287 var cs = this.el.getBox(true);
39290 this.currentSize.width == cs.width &&
39291 this.currentSize.x == cs.x &&
39292 this.currentSize.height == cs.height &&
39293 this.currentSize.y == cs.y
39295 Roo.log("no change in with or X or Y");
39299 this.currentSize = cs;
39305 layout : function()
39307 this._resetLayout();
39309 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
39311 this.layoutItems( isInstant );
39313 this._isLayoutInited = true;
39315 this.fireEvent('layout', this);
39319 _resetLayout : function()
39321 if(this.isHorizontal){
39322 this.horizontalMeasureColumns();
39326 this.verticalMeasureColumns();
39330 verticalMeasureColumns : function()
39332 this.getContainerWidth();
39334 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
39335 // this.colWidth = Math.floor(this.containerWidth * 0.8);
39339 var boxWidth = this.boxWidth + this.padWidth;
39341 if(this.containerWidth < this.boxWidth){
39342 boxWidth = this.containerWidth
39345 var containerWidth = this.containerWidth;
39347 var cols = Math.floor(containerWidth / boxWidth);
39349 this.cols = Math.max( cols, 1 );
39351 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
39353 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
39355 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
39357 this.colWidth = boxWidth + avail - this.padWidth;
39359 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
39360 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
39363 horizontalMeasureColumns : function()
39365 this.getContainerWidth();
39367 var boxWidth = this.boxWidth;
39369 if(this.containerWidth < boxWidth){
39370 boxWidth = this.containerWidth;
39373 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
39375 this.el.setHeight(boxWidth);
39379 getContainerWidth : function()
39381 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
39384 layoutItems : function( isInstant )
39386 Roo.log(this.bricks);
39388 var items = Roo.apply([], this.bricks);
39390 if(this.isHorizontal){
39391 this._horizontalLayoutItems( items , isInstant );
39395 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
39396 // this._verticalAlternativeLayoutItems( items , isInstant );
39400 this._verticalLayoutItems( items , isInstant );
39404 _verticalLayoutItems : function ( items , isInstant)
39406 if ( !items || !items.length ) {
39411 ['xs', 'xs', 'xs', 'tall'],
39412 ['xs', 'xs', 'tall'],
39413 ['xs', 'xs', 'sm'],
39414 ['xs', 'xs', 'xs'],
39420 ['sm', 'xs', 'xs'],
39424 ['tall', 'xs', 'xs', 'xs'],
39425 ['tall', 'xs', 'xs'],
39437 Roo.each(items, function(item, k){
39439 switch (item.size) {
39440 // these layouts take up a full box,
39451 boxes.push([item]);
39474 var filterPattern = function(box, length)
39482 var pattern = box.slice(0, length);
39486 Roo.each(pattern, function(i){
39487 format.push(i.size);
39490 Roo.each(standard, function(s){
39492 if(String(s) != String(format)){
39501 if(!match && length == 1){
39506 filterPattern(box, length - 1);
39510 queue.push(pattern);
39512 box = box.slice(length, box.length);
39514 filterPattern(box, 4);
39520 Roo.each(boxes, function(box, k){
39526 if(box.length == 1){
39531 filterPattern(box, 4);
39535 this._processVerticalLayoutQueue( queue, isInstant );
39539 // _verticalAlternativeLayoutItems : function( items , isInstant )
39541 // if ( !items || !items.length ) {
39545 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
39549 _horizontalLayoutItems : function ( items , isInstant)
39551 if ( !items || !items.length || items.length < 3) {
39557 var eItems = items.slice(0, 3);
39559 items = items.slice(3, items.length);
39562 ['xs', 'xs', 'xs', 'wide'],
39563 ['xs', 'xs', 'wide'],
39564 ['xs', 'xs', 'sm'],
39565 ['xs', 'xs', 'xs'],
39571 ['sm', 'xs', 'xs'],
39575 ['wide', 'xs', 'xs', 'xs'],
39576 ['wide', 'xs', 'xs'],
39589 Roo.each(items, function(item, k){
39591 switch (item.size) {
39602 boxes.push([item]);
39626 var filterPattern = function(box, length)
39634 var pattern = box.slice(0, length);
39638 Roo.each(pattern, function(i){
39639 format.push(i.size);
39642 Roo.each(standard, function(s){
39644 if(String(s) != String(format)){
39653 if(!match && length == 1){
39658 filterPattern(box, length - 1);
39662 queue.push(pattern);
39664 box = box.slice(length, box.length);
39666 filterPattern(box, 4);
39672 Roo.each(boxes, function(box, k){
39678 if(box.length == 1){
39683 filterPattern(box, 4);
39690 var pos = this.el.getBox(true);
39694 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39696 var hit_end = false;
39698 Roo.each(queue, function(box){
39702 Roo.each(box, function(b){
39704 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39714 Roo.each(box, function(b){
39716 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39719 mx = Math.max(mx, b.x);
39723 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
39727 Roo.each(box, function(b){
39729 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39743 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
39746 /** Sets position of item in DOM
39747 * @param {Element} item
39748 * @param {Number} x - horizontal position
39749 * @param {Number} y - vertical position
39750 * @param {Boolean} isInstant - disables transitions
39752 _processVerticalLayoutQueue : function( queue, isInstant )
39754 var pos = this.el.getBox(true);
39759 for (var i = 0; i < this.cols; i++){
39763 Roo.each(queue, function(box, k){
39765 var col = k % this.cols;
39767 Roo.each(box, function(b,kk){
39769 b.el.position('absolute');
39771 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39772 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39774 if(b.size == 'md-left' || b.size == 'md-right'){
39775 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39776 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39779 b.el.setWidth(width);
39780 b.el.setHeight(height);
39782 b.el.select('iframe',true).setSize(width,height);
39786 for (var i = 0; i < this.cols; i++){
39788 if(maxY[i] < maxY[col]){
39793 col = Math.min(col, i);
39797 x = pos.x + col * (this.colWidth + this.padWidth);
39801 var positions = [];
39803 switch (box.length){
39805 positions = this.getVerticalOneBoxColPositions(x, y, box);
39808 positions = this.getVerticalTwoBoxColPositions(x, y, box);
39811 positions = this.getVerticalThreeBoxColPositions(x, y, box);
39814 positions = this.getVerticalFourBoxColPositions(x, y, box);
39820 Roo.each(box, function(b,kk){
39822 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39824 var sz = b.el.getSize();
39826 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
39834 for (var i = 0; i < this.cols; i++){
39835 mY = Math.max(mY, maxY[i]);
39838 this.el.setHeight(mY - pos.y);
39842 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
39844 // var pos = this.el.getBox(true);
39847 // var maxX = pos.right;
39849 // var maxHeight = 0;
39851 // Roo.each(items, function(item, k){
39855 // item.el.position('absolute');
39857 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
39859 // item.el.setWidth(width);
39861 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
39863 // item.el.setHeight(height);
39866 // item.el.setXY([x, y], isInstant ? false : true);
39868 // item.el.setXY([maxX - width, y], isInstant ? false : true);
39871 // y = y + height + this.alternativePadWidth;
39873 // maxHeight = maxHeight + height + this.alternativePadWidth;
39877 // this.el.setHeight(maxHeight);
39881 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
39883 var pos = this.el.getBox(true);
39888 var maxX = pos.right;
39890 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
39892 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39894 Roo.each(queue, function(box, k){
39896 Roo.each(box, function(b, kk){
39898 b.el.position('absolute');
39900 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39901 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39903 if(b.size == 'md-left' || b.size == 'md-right'){
39904 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39905 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39908 b.el.setWidth(width);
39909 b.el.setHeight(height);
39917 var positions = [];
39919 switch (box.length){
39921 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
39924 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
39927 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
39930 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
39936 Roo.each(box, function(b,kk){
39938 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39940 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
39948 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39950 Roo.each(eItems, function(b,k){
39952 b.size = (k == 0) ? 'sm' : 'xs';
39953 b.x = (k == 0) ? 2 : 1;
39954 b.y = (k == 0) ? 2 : 1;
39956 b.el.position('absolute');
39958 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39960 b.el.setWidth(width);
39962 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39964 b.el.setHeight(height);
39968 var positions = [];
39971 x : maxX - this.unitWidth * 2 - this.gutter,
39976 x : maxX - this.unitWidth,
39977 y : minY + (this.unitWidth + this.gutter) * 2
39981 x : maxX - this.unitWidth * 3 - this.gutter * 2,
39985 Roo.each(eItems, function(b,k){
39987 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39993 getVerticalOneBoxColPositions : function(x, y, box)
39997 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39999 if(box[0].size == 'md-left'){
40003 if(box[0].size == 'md-right'){
40008 x : x + (this.unitWidth + this.gutter) * rand,
40015 getVerticalTwoBoxColPositions : function(x, y, box)
40019 if(box[0].size == 'xs'){
40023 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
40027 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
40041 x : x + (this.unitWidth + this.gutter) * 2,
40042 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
40049 getVerticalThreeBoxColPositions : function(x, y, box)
40053 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
40061 x : x + (this.unitWidth + this.gutter) * 1,
40066 x : x + (this.unitWidth + this.gutter) * 2,
40074 if(box[0].size == 'xs' && box[1].size == 'xs'){
40083 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
40087 x : x + (this.unitWidth + this.gutter) * 1,
40101 x : x + (this.unitWidth + this.gutter) * 2,
40106 x : x + (this.unitWidth + this.gutter) * 2,
40107 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
40114 getVerticalFourBoxColPositions : function(x, y, box)
40118 if(box[0].size == 'xs'){
40127 y : y + (this.unitHeight + this.gutter) * 1
40132 y : y + (this.unitHeight + this.gutter) * 2
40136 x : x + (this.unitWidth + this.gutter) * 1,
40150 x : x + (this.unitWidth + this.gutter) * 2,
40155 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
40156 y : y + (this.unitHeight + this.gutter) * 1
40160 x : x + (this.unitWidth + this.gutter) * 2,
40161 y : y + (this.unitWidth + this.gutter) * 2
40168 getHorizontalOneBoxColPositions : function(maxX, minY, box)
40172 if(box[0].size == 'md-left'){
40174 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
40181 if(box[0].size == 'md-right'){
40183 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
40184 y : minY + (this.unitWidth + this.gutter) * 1
40190 var rand = Math.floor(Math.random() * (4 - box[0].y));
40193 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40194 y : minY + (this.unitWidth + this.gutter) * rand
40201 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
40205 if(box[0].size == 'xs'){
40208 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40213 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40214 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
40222 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40227 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40228 y : minY + (this.unitWidth + this.gutter) * 2
40235 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
40239 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
40242 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40247 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40248 y : minY + (this.unitWidth + this.gutter) * 1
40252 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40253 y : minY + (this.unitWidth + this.gutter) * 2
40260 if(box[0].size == 'xs' && box[1].size == 'xs'){
40263 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40268 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40273 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40274 y : minY + (this.unitWidth + this.gutter) * 1
40282 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40287 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40288 y : minY + (this.unitWidth + this.gutter) * 2
40292 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40293 y : minY + (this.unitWidth + this.gutter) * 2
40300 getHorizontalFourBoxColPositions : function(maxX, minY, box)
40304 if(box[0].size == 'xs'){
40307 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40312 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40317 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),
40322 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
40323 y : minY + (this.unitWidth + this.gutter) * 1
40331 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40336 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40337 y : minY + (this.unitWidth + this.gutter) * 2
40341 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40342 y : minY + (this.unitWidth + this.gutter) * 2
40346 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),
40347 y : minY + (this.unitWidth + this.gutter) * 2
40355 * remove a Masonry Brick
40356 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
40358 removeBrick : function(brick_id)
40364 for (var i = 0; i<this.bricks.length; i++) {
40365 if (this.bricks[i].id == brick_id) {
40366 this.bricks.splice(i,1);
40367 this.el.dom.removeChild(Roo.get(brick_id).dom);
40374 * adds a Masonry Brick
40375 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40377 addBrick : function(cfg)
40379 var cn = new Roo.bootstrap.MasonryBrick(cfg);
40380 //this.register(cn);
40381 cn.parentId = this.id;
40382 cn.render(this.el);
40387 * register a Masonry Brick
40388 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40391 register : function(brick)
40393 this.bricks.push(brick);
40394 brick.masonryId = this.id;
40398 * clear all the Masonry Brick
40400 clearAll : function()
40403 //this.getChildContainer().dom.innerHTML = "";
40404 this.el.dom.innerHTML = '';
40407 getSelected : function()
40409 if (!this.selectedBrick) {
40413 return this.selectedBrick;
40417 Roo.apply(Roo.bootstrap.LayoutMasonry, {
40421 * register a Masonry Layout
40422 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
40425 register : function(layout)
40427 this.groups[layout.id] = layout;
40430 * fetch a Masonry Layout based on the masonry layout ID
40431 * @param {string} the masonry layout to add
40432 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
40435 get: function(layout_id) {
40436 if (typeof(this.groups[layout_id]) == 'undefined') {
40439 return this.groups[layout_id] ;
40451 * http://masonry.desandro.com
40453 * The idea is to render all the bricks based on vertical width...
40455 * The original code extends 'outlayer' - we might need to use that....
40461 * @class Roo.bootstrap.LayoutMasonryAuto
40462 * @extends Roo.bootstrap.Component
40463 * Bootstrap Layout Masonry class
40466 * Create a new Element
40467 * @param {Object} config The config object
40470 Roo.bootstrap.LayoutMasonryAuto = function(config){
40471 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
40474 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
40477 * @cfg {Boolean} isFitWidth - resize the width..
40479 isFitWidth : false, // options..
40481 * @cfg {Boolean} isOriginLeft = left align?
40483 isOriginLeft : true,
40485 * @cfg {Boolean} isOriginTop = top align?
40487 isOriginTop : false,
40489 * @cfg {Boolean} isLayoutInstant = no animation?
40491 isLayoutInstant : false, // needed?
40493 * @cfg {Boolean} isResizingContainer = not sure if this is used..
40495 isResizingContainer : true,
40497 * @cfg {Number} columnWidth width of the columns
40503 * @cfg {Number} maxCols maximum number of columns
40508 * @cfg {Number} padHeight padding below box..
40514 * @cfg {Boolean} isAutoInitial defalut true
40517 isAutoInitial : true,
40523 initialColumnWidth : 0,
40524 currentSize : null,
40526 colYs : null, // array.
40533 bricks: null, //CompositeElement
40534 cols : 0, // array?
40535 // element : null, // wrapped now this.el
40536 _isLayoutInited : null,
40539 getAutoCreate : function(){
40543 cls: 'blog-masonary-wrapper ' + this.cls,
40545 cls : 'mas-boxes masonary'
40552 getChildContainer: function( )
40554 if (this.boxesEl) {
40555 return this.boxesEl;
40558 this.boxesEl = this.el.select('.mas-boxes').first();
40560 return this.boxesEl;
40564 initEvents : function()
40568 if(this.isAutoInitial){
40569 Roo.log('hook children rendered');
40570 this.on('childrenrendered', function() {
40571 Roo.log('children rendered');
40578 initial : function()
40580 this.reloadItems();
40582 this.currentSize = this.el.getBox(true);
40584 /// was window resize... - let's see if this works..
40585 Roo.EventManager.onWindowResize(this.resize, this);
40587 if(!this.isAutoInitial){
40592 this.layout.defer(500,this);
40595 reloadItems: function()
40597 this.bricks = this.el.select('.masonry-brick', true);
40599 this.bricks.each(function(b) {
40600 //Roo.log(b.getSize());
40601 if (!b.attr('originalwidth')) {
40602 b.attr('originalwidth', b.getSize().width);
40607 Roo.log(this.bricks.elements.length);
40610 resize : function()
40613 var cs = this.el.getBox(true);
40615 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
40616 Roo.log("no change in with or X");
40619 this.currentSize = cs;
40623 layout : function()
40626 this._resetLayout();
40627 //this._manageStamps();
40629 // don't animate first layout
40630 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
40631 this.layoutItems( isInstant );
40633 // flag for initalized
40634 this._isLayoutInited = true;
40637 layoutItems : function( isInstant )
40639 //var items = this._getItemsForLayout( this.items );
40640 // original code supports filtering layout items.. we just ignore it..
40642 this._layoutItems( this.bricks , isInstant );
40644 this._postLayout();
40646 _layoutItems : function ( items , isInstant)
40648 //this.fireEvent( 'layout', this, items );
40651 if ( !items || !items.elements.length ) {
40652 // no items, emit event with empty array
40657 items.each(function(item) {
40658 Roo.log("layout item");
40660 // get x/y object from method
40661 var position = this._getItemLayoutPosition( item );
40663 position.item = item;
40664 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
40665 queue.push( position );
40668 this._processLayoutQueue( queue );
40670 /** Sets position of item in DOM
40671 * @param {Element} item
40672 * @param {Number} x - horizontal position
40673 * @param {Number} y - vertical position
40674 * @param {Boolean} isInstant - disables transitions
40676 _processLayoutQueue : function( queue )
40678 for ( var i=0, len = queue.length; i < len; i++ ) {
40679 var obj = queue[i];
40680 obj.item.position('absolute');
40681 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
40687 * Any logic you want to do after each layout,
40688 * i.e. size the container
40690 _postLayout : function()
40692 this.resizeContainer();
40695 resizeContainer : function()
40697 if ( !this.isResizingContainer ) {
40700 var size = this._getContainerSize();
40702 this.el.setSize(size.width,size.height);
40703 this.boxesEl.setSize(size.width,size.height);
40709 _resetLayout : function()
40711 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
40712 this.colWidth = this.el.getWidth();
40713 //this.gutter = this.el.getWidth();
40715 this.measureColumns();
40721 this.colYs.push( 0 );
40727 measureColumns : function()
40729 this.getContainerWidth();
40730 // if columnWidth is 0, default to outerWidth of first item
40731 if ( !this.columnWidth ) {
40732 var firstItem = this.bricks.first();
40733 Roo.log(firstItem);
40734 this.columnWidth = this.containerWidth;
40735 if (firstItem && firstItem.attr('originalwidth') ) {
40736 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
40738 // columnWidth fall back to item of first element
40739 Roo.log("set column width?");
40740 this.initialColumnWidth = this.columnWidth ;
40742 // if first elem has no width, default to size of container
40747 if (this.initialColumnWidth) {
40748 this.columnWidth = this.initialColumnWidth;
40753 // column width is fixed at the top - however if container width get's smaller we should
40756 // this bit calcs how man columns..
40758 var columnWidth = this.columnWidth += this.gutter;
40760 // calculate columns
40761 var containerWidth = this.containerWidth + this.gutter;
40763 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
40764 // fix rounding errors, typically with gutters
40765 var excess = columnWidth - containerWidth % columnWidth;
40768 // if overshoot is less than a pixel, round up, otherwise floor it
40769 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
40770 cols = Math[ mathMethod ]( cols );
40771 this.cols = Math.max( cols, 1 );
40772 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
40774 // padding positioning..
40775 var totalColWidth = this.cols * this.columnWidth;
40776 var padavail = this.containerWidth - totalColWidth;
40777 // so for 2 columns - we need 3 'pads'
40779 var padNeeded = (1+this.cols) * this.padWidth;
40781 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
40783 this.columnWidth += padExtra
40784 //this.padWidth = Math.floor(padavail / ( this.cols));
40786 // adjust colum width so that padding is fixed??
40788 // we have 3 columns ... total = width * 3
40789 // we have X left over... that should be used by
40791 //if (this.expandC) {
40799 getContainerWidth : function()
40801 /* // container is parent if fit width
40802 var container = this.isFitWidth ? this.element.parentNode : this.element;
40803 // check that this.size and size are there
40804 // IE8 triggers resize on body size change, so they might not be
40806 var size = getSize( container ); //FIXME
40807 this.containerWidth = size && size.innerWidth; //FIXME
40810 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
40814 _getItemLayoutPosition : function( item ) // what is item?
40816 // we resize the item to our columnWidth..
40818 item.setWidth(this.columnWidth);
40819 item.autoBoxAdjust = false;
40821 var sz = item.getSize();
40823 // how many columns does this brick span
40824 var remainder = this.containerWidth % this.columnWidth;
40826 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
40827 // round if off by 1 pixel, otherwise use ceil
40828 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
40829 colSpan = Math.min( colSpan, this.cols );
40831 // normally this should be '1' as we dont' currently allow multi width columns..
40833 var colGroup = this._getColGroup( colSpan );
40834 // get the minimum Y value from the columns
40835 var minimumY = Math.min.apply( Math, colGroup );
40836 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
40838 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
40840 // position the brick
40842 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
40843 y: this.currentSize.y + minimumY + this.padHeight
40847 // apply setHeight to necessary columns
40848 var setHeight = minimumY + sz.height + this.padHeight;
40849 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
40851 var setSpan = this.cols + 1 - colGroup.length;
40852 for ( var i = 0; i < setSpan; i++ ) {
40853 this.colYs[ shortColIndex + i ] = setHeight ;
40860 * @param {Number} colSpan - number of columns the element spans
40861 * @returns {Array} colGroup
40863 _getColGroup : function( colSpan )
40865 if ( colSpan < 2 ) {
40866 // if brick spans only one column, use all the column Ys
40871 // how many different places could this brick fit horizontally
40872 var groupCount = this.cols + 1 - colSpan;
40873 // for each group potential horizontal position
40874 for ( var i = 0; i < groupCount; i++ ) {
40875 // make an array of colY values for that one group
40876 var groupColYs = this.colYs.slice( i, i + colSpan );
40877 // and get the max value of the array
40878 colGroup[i] = Math.max.apply( Math, groupColYs );
40883 _manageStamp : function( stamp )
40885 var stampSize = stamp.getSize();
40886 var offset = stamp.getBox();
40887 // get the columns that this stamp affects
40888 var firstX = this.isOriginLeft ? offset.x : offset.right;
40889 var lastX = firstX + stampSize.width;
40890 var firstCol = Math.floor( firstX / this.columnWidth );
40891 firstCol = Math.max( 0, firstCol );
40893 var lastCol = Math.floor( lastX / this.columnWidth );
40894 // lastCol should not go over if multiple of columnWidth #425
40895 lastCol -= lastX % this.columnWidth ? 0 : 1;
40896 lastCol = Math.min( this.cols - 1, lastCol );
40898 // set colYs to bottom of the stamp
40899 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
40902 for ( var i = firstCol; i <= lastCol; i++ ) {
40903 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
40908 _getContainerSize : function()
40910 this.maxY = Math.max.apply( Math, this.colYs );
40915 if ( this.isFitWidth ) {
40916 size.width = this._getContainerFitWidth();
40922 _getContainerFitWidth : function()
40924 var unusedCols = 0;
40925 // count unused columns
40928 if ( this.colYs[i] !== 0 ) {
40933 // fit container to columns that have been used
40934 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
40937 needsResizeLayout : function()
40939 var previousWidth = this.containerWidth;
40940 this.getContainerWidth();
40941 return previousWidth !== this.containerWidth;
40956 * @class Roo.bootstrap.MasonryBrick
40957 * @extends Roo.bootstrap.Component
40958 * Bootstrap MasonryBrick class
40961 * Create a new MasonryBrick
40962 * @param {Object} config The config object
40965 Roo.bootstrap.MasonryBrick = function(config){
40967 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40969 Roo.bootstrap.MasonryBrick.register(this);
40975 * When a MasonryBrick is clcik
40976 * @param {Roo.bootstrap.MasonryBrick} this
40977 * @param {Roo.EventObject} e
40983 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
40986 * @cfg {String} title
40990 * @cfg {String} html
40994 * @cfg {String} bgimage
40998 * @cfg {String} videourl
41002 * @cfg {String} cls
41006 * @cfg {String} href
41010 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
41015 * @cfg {String} placetitle (center|bottom)
41020 * @cfg {Boolean} isFitContainer defalut true
41022 isFitContainer : true,
41025 * @cfg {Boolean} preventDefault defalut false
41027 preventDefault : false,
41030 * @cfg {Boolean} inverse defalut false
41032 maskInverse : false,
41034 getAutoCreate : function()
41036 if(!this.isFitContainer){
41037 return this.getSplitAutoCreate();
41040 var cls = 'masonry-brick masonry-brick-full';
41042 if(this.href.length){
41043 cls += ' masonry-brick-link';
41046 if(this.bgimage.length){
41047 cls += ' masonry-brick-image';
41050 if(this.maskInverse){
41051 cls += ' mask-inverse';
41054 if(!this.html.length && !this.maskInverse && !this.videourl.length){
41055 cls += ' enable-mask';
41059 cls += ' masonry-' + this.size + '-brick';
41062 if(this.placetitle.length){
41064 switch (this.placetitle) {
41066 cls += ' masonry-center-title';
41069 cls += ' masonry-bottom-title';
41076 if(!this.html.length && !this.bgimage.length){
41077 cls += ' masonry-center-title';
41080 if(!this.html.length && this.bgimage.length){
41081 cls += ' masonry-bottom-title';
41086 cls += ' ' + this.cls;
41090 tag: (this.href.length) ? 'a' : 'div',
41095 cls: 'masonry-brick-mask'
41099 cls: 'masonry-brick-paragraph',
41105 if(this.href.length){
41106 cfg.href = this.href;
41109 var cn = cfg.cn[1].cn;
41111 if(this.title.length){
41114 cls: 'masonry-brick-title',
41119 if(this.html.length){
41122 cls: 'masonry-brick-text',
41127 if (!this.title.length && !this.html.length) {
41128 cfg.cn[1].cls += ' hide';
41131 if(this.bgimage.length){
41134 cls: 'masonry-brick-image-view',
41139 if(this.videourl.length){
41140 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
41141 // youtube support only?
41144 cls: 'masonry-brick-image-view',
41147 allowfullscreen : true
41155 getSplitAutoCreate : function()
41157 var cls = 'masonry-brick masonry-brick-split';
41159 if(this.href.length){
41160 cls += ' masonry-brick-link';
41163 if(this.bgimage.length){
41164 cls += ' masonry-brick-image';
41168 cls += ' masonry-' + this.size + '-brick';
41171 switch (this.placetitle) {
41173 cls += ' masonry-center-title';
41176 cls += ' masonry-bottom-title';
41179 if(!this.bgimage.length){
41180 cls += ' masonry-center-title';
41183 if(this.bgimage.length){
41184 cls += ' masonry-bottom-title';
41190 cls += ' ' + this.cls;
41194 tag: (this.href.length) ? 'a' : 'div',
41199 cls: 'masonry-brick-split-head',
41203 cls: 'masonry-brick-paragraph',
41210 cls: 'masonry-brick-split-body',
41216 if(this.href.length){
41217 cfg.href = this.href;
41220 if(this.title.length){
41221 cfg.cn[0].cn[0].cn.push({
41223 cls: 'masonry-brick-title',
41228 if(this.html.length){
41229 cfg.cn[1].cn.push({
41231 cls: 'masonry-brick-text',
41236 if(this.bgimage.length){
41237 cfg.cn[0].cn.push({
41239 cls: 'masonry-brick-image-view',
41244 if(this.videourl.length){
41245 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
41246 // youtube support only?
41247 cfg.cn[0].cn.cn.push({
41249 cls: 'masonry-brick-image-view',
41252 allowfullscreen : true
41259 initEvents: function()
41261 switch (this.size) {
41294 this.el.on('touchstart', this.onTouchStart, this);
41295 this.el.on('touchmove', this.onTouchMove, this);
41296 this.el.on('touchend', this.onTouchEnd, this);
41297 this.el.on('contextmenu', this.onContextMenu, this);
41299 this.el.on('mouseenter' ,this.enter, this);
41300 this.el.on('mouseleave', this.leave, this);
41301 this.el.on('click', this.onClick, this);
41304 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
41305 this.parent().bricks.push(this);
41310 onClick: function(e, el)
41312 var time = this.endTimer - this.startTimer;
41313 // Roo.log(e.preventDefault());
41316 e.preventDefault();
41321 if(!this.preventDefault){
41325 e.preventDefault();
41327 if (this.activeClass != '') {
41328 this.selectBrick();
41331 this.fireEvent('click', this, e);
41334 enter: function(e, el)
41336 e.preventDefault();
41338 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
41342 if(this.bgimage.length && this.html.length){
41343 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
41347 leave: function(e, el)
41349 e.preventDefault();
41351 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
41355 if(this.bgimage.length && this.html.length){
41356 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
41360 onTouchStart: function(e, el)
41362 // e.preventDefault();
41364 this.touchmoved = false;
41366 if(!this.isFitContainer){
41370 if(!this.bgimage.length || !this.html.length){
41374 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
41376 this.timer = new Date().getTime();
41380 onTouchMove: function(e, el)
41382 this.touchmoved = true;
41385 onContextMenu : function(e,el)
41387 e.preventDefault();
41388 e.stopPropagation();
41392 onTouchEnd: function(e, el)
41394 // e.preventDefault();
41396 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
41403 if(!this.bgimage.length || !this.html.length){
41405 if(this.href.length){
41406 window.location.href = this.href;
41412 if(!this.isFitContainer){
41416 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
41418 window.location.href = this.href;
41421 //selection on single brick only
41422 selectBrick : function() {
41424 if (!this.parentId) {
41428 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
41429 var index = m.selectedBrick.indexOf(this.id);
41432 m.selectedBrick.splice(index,1);
41433 this.el.removeClass(this.activeClass);
41437 for(var i = 0; i < m.selectedBrick.length; i++) {
41438 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
41439 b.el.removeClass(b.activeClass);
41442 m.selectedBrick = [];
41444 m.selectedBrick.push(this.id);
41445 this.el.addClass(this.activeClass);
41449 isSelected : function(){
41450 return this.el.hasClass(this.activeClass);
41455 Roo.apply(Roo.bootstrap.MasonryBrick, {
41458 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
41460 * register a Masonry Brick
41461 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
41464 register : function(brick)
41466 //this.groups[brick.id] = brick;
41467 this.groups.add(brick.id, brick);
41470 * fetch a masonry brick based on the masonry brick ID
41471 * @param {string} the masonry brick to add
41472 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
41475 get: function(brick_id)
41477 // if (typeof(this.groups[brick_id]) == 'undefined') {
41480 // return this.groups[brick_id] ;
41482 if(this.groups.key(brick_id)) {
41483 return this.groups.key(brick_id);
41501 * @class Roo.bootstrap.Brick
41502 * @extends Roo.bootstrap.Component
41503 * Bootstrap Brick class
41506 * Create a new Brick
41507 * @param {Object} config The config object
41510 Roo.bootstrap.Brick = function(config){
41511 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
41517 * When a Brick is click
41518 * @param {Roo.bootstrap.Brick} this
41519 * @param {Roo.EventObject} e
41525 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
41528 * @cfg {String} title
41532 * @cfg {String} html
41536 * @cfg {String} bgimage
41540 * @cfg {String} cls
41544 * @cfg {String} href
41548 * @cfg {String} video
41552 * @cfg {Boolean} square
41556 getAutoCreate : function()
41558 var cls = 'roo-brick';
41560 if(this.href.length){
41561 cls += ' roo-brick-link';
41564 if(this.bgimage.length){
41565 cls += ' roo-brick-image';
41568 if(!this.html.length && !this.bgimage.length){
41569 cls += ' roo-brick-center-title';
41572 if(!this.html.length && this.bgimage.length){
41573 cls += ' roo-brick-bottom-title';
41577 cls += ' ' + this.cls;
41581 tag: (this.href.length) ? 'a' : 'div',
41586 cls: 'roo-brick-paragraph',
41592 if(this.href.length){
41593 cfg.href = this.href;
41596 var cn = cfg.cn[0].cn;
41598 if(this.title.length){
41601 cls: 'roo-brick-title',
41606 if(this.html.length){
41609 cls: 'roo-brick-text',
41616 if(this.bgimage.length){
41619 cls: 'roo-brick-image-view',
41627 initEvents: function()
41629 if(this.title.length || this.html.length){
41630 this.el.on('mouseenter' ,this.enter, this);
41631 this.el.on('mouseleave', this.leave, this);
41634 Roo.EventManager.onWindowResize(this.resize, this);
41636 if(this.bgimage.length){
41637 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
41638 this.imageEl.on('load', this.onImageLoad, this);
41645 onImageLoad : function()
41650 resize : function()
41652 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
41654 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
41656 if(this.bgimage.length){
41657 var image = this.el.select('.roo-brick-image-view', true).first();
41659 image.setWidth(paragraph.getWidth());
41662 image.setHeight(paragraph.getWidth());
41665 this.el.setHeight(image.getHeight());
41666 paragraph.setHeight(image.getHeight());
41672 enter: function(e, el)
41674 e.preventDefault();
41676 if(this.bgimage.length){
41677 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
41678 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
41682 leave: function(e, el)
41684 e.preventDefault();
41686 if(this.bgimage.length){
41687 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
41688 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
41703 * @class Roo.bootstrap.form.NumberField
41704 * @extends Roo.bootstrap.form.Input
41705 * Bootstrap NumberField class
41711 * Create a new NumberField
41712 * @param {Object} config The config object
41715 Roo.bootstrap.form.NumberField = function(config){
41716 Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
41719 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
41722 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41724 allowDecimals : true,
41726 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41728 decimalSeparator : ".",
41730 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41732 decimalPrecision : 2,
41734 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41736 allowNegative : true,
41739 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41743 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41745 minValue : Number.NEGATIVE_INFINITY,
41747 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41749 maxValue : Number.MAX_VALUE,
41751 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41753 minText : "The minimum value for this field is {0}",
41755 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41757 maxText : "The maximum value for this field is {0}",
41759 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
41760 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41762 nanText : "{0} is not a valid number",
41764 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41766 thousandsDelimiter : false,
41768 * @cfg {String} valueAlign alignment of value
41770 valueAlign : "left",
41772 getAutoCreate : function()
41774 var hiddenInput = {
41778 cls: 'hidden-number-input'
41782 hiddenInput.name = this.name;
41787 var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
41789 this.name = hiddenInput.name;
41791 if(cfg.cn.length > 0) {
41792 cfg.cn.push(hiddenInput);
41799 initEvents : function()
41801 Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
41803 var allowed = "0123456789";
41805 if(this.allowDecimals){
41806 allowed += this.decimalSeparator;
41809 if(this.allowNegative){
41813 if(this.thousandsDelimiter) {
41817 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41819 var keyPress = function(e){
41821 var k = e.getKey();
41823 var c = e.getCharCode();
41826 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41827 allowed.indexOf(String.fromCharCode(c)) === -1
41833 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41837 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41842 this.el.on("keypress", keyPress, this);
41845 validateValue : function(value)
41848 if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
41852 var num = this.parseValue(value);
41855 this.markInvalid(String.format(this.nanText, value));
41859 if(num < this.minValue){
41860 this.markInvalid(String.format(this.minText, this.minValue));
41864 if(num > this.maxValue){
41865 this.markInvalid(String.format(this.maxText, this.maxValue));
41872 getValue : function()
41874 var v = this.hiddenEl().getValue();
41876 return this.fixPrecision(this.parseValue(v));
41879 parseValue : function(value)
41881 if(this.thousandsDelimiter) {
41883 r = new RegExp(",", "g");
41884 value = value.replace(r, "");
41887 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41888 return isNaN(value) ? '' : value;
41891 fixPrecision : function(value)
41893 if(this.thousandsDelimiter) {
41895 r = new RegExp(",", "g");
41896 value = value.replace(r, "");
41899 var nan = isNaN(value);
41901 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41902 return nan ? '' : value;
41904 return parseFloat(value).toFixed(this.decimalPrecision);
41907 setValue : function(v)
41909 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41915 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41917 this.inputEl().dom.value = (v == '') ? '' :
41918 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41920 if(!this.allowZero && v === '0') {
41921 this.hiddenEl().dom.value = '';
41922 this.inputEl().dom.value = '';
41929 decimalPrecisionFcn : function(v)
41931 return Math.floor(v);
41934 beforeBlur : function()
41936 var v = this.parseValue(this.getRawValue());
41938 if(v || v === 0 || v === ''){
41943 hiddenEl : function()
41945 return this.el.select('input.hidden-number-input',true).first();
41957 * @class Roo.bootstrap.DocumentSlider
41958 * @extends Roo.bootstrap.Component
41959 * Bootstrap DocumentSlider class
41962 * Create a new DocumentViewer
41963 * @param {Object} config The config object
41966 Roo.bootstrap.DocumentSlider = function(config){
41967 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41974 * Fire after initEvent
41975 * @param {Roo.bootstrap.DocumentSlider} this
41980 * Fire after update
41981 * @param {Roo.bootstrap.DocumentSlider} this
41987 * @param {Roo.bootstrap.DocumentSlider} this
41993 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
41999 getAutoCreate : function()
42003 cls : 'roo-document-slider',
42007 cls : 'roo-document-slider-header',
42011 cls : 'roo-document-slider-header-title'
42017 cls : 'roo-document-slider-body',
42021 cls : 'roo-document-slider-prev',
42025 cls : 'fa fa-chevron-left'
42031 cls : 'roo-document-slider-thumb',
42035 cls : 'roo-document-slider-image'
42041 cls : 'roo-document-slider-next',
42045 cls : 'fa fa-chevron-right'
42057 initEvents : function()
42059 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
42060 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
42062 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
42063 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
42065 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
42066 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
42068 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
42069 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
42071 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
42072 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
42074 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
42075 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
42077 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
42078 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
42080 this.thumbEl.on('click', this.onClick, this);
42082 this.prevIndicator.on('click', this.prev, this);
42084 this.nextIndicator.on('click', this.next, this);
42088 initial : function()
42090 if(this.files.length){
42091 this.indicator = 1;
42095 this.fireEvent('initial', this);
42098 update : function()
42100 this.imageEl.attr('src', this.files[this.indicator - 1]);
42102 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
42104 this.prevIndicator.show();
42106 if(this.indicator == 1){
42107 this.prevIndicator.hide();
42110 this.nextIndicator.show();
42112 if(this.indicator == this.files.length){
42113 this.nextIndicator.hide();
42116 this.thumbEl.scrollTo('top');
42118 this.fireEvent('update', this);
42121 onClick : function(e)
42123 e.preventDefault();
42125 this.fireEvent('click', this);
42130 e.preventDefault();
42132 this.indicator = Math.max(1, this.indicator - 1);
42139 e.preventDefault();
42141 this.indicator = Math.min(this.files.length, this.indicator + 1);
42155 * @class Roo.bootstrap.form.RadioSet
42156 * @extends Roo.bootstrap.form.Input
42157 * @children Roo.bootstrap.form.Radio
42158 * Bootstrap RadioSet class
42159 * @cfg {String} indicatorpos (left|right) default left
42160 * @cfg {Boolean} inline (true|false) inline the element (default true)
42161 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
42163 * Create a new RadioSet
42164 * @param {Object} config The config object
42167 Roo.bootstrap.form.RadioSet = function(config){
42169 Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
42173 Roo.bootstrap.form.RadioSet.register(this);
42178 * Fires when the element is checked or unchecked.
42179 * @param {Roo.bootstrap.form.RadioSet} this This radio
42180 * @param {Roo.bootstrap.form.Radio} item The checked item
42185 * Fires when the element is click.
42186 * @param {Roo.bootstrap.form.RadioSet} this This radio set
42187 * @param {Roo.bootstrap.form.Radio} item The checked item
42188 * @param {Roo.EventObject} e The event object
42195 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input, {
42203 indicatorpos : 'left',
42205 getAutoCreate : function()
42209 cls : 'roo-radio-set-label',
42213 html : this.fieldLabel
42217 if (Roo.bootstrap.version == 3) {
42220 if(this.indicatorpos == 'left'){
42223 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
42224 tooltip : 'This field is required'
42229 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
42230 tooltip : 'This field is required'
42236 cls : 'roo-radio-set-items'
42239 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
42241 if (align === 'left' && this.fieldLabel.length) {
42244 cls : "roo-radio-set-right",
42250 if(this.labelWidth > 12){
42251 label.style = "width: " + this.labelWidth + 'px';
42254 if(this.labelWidth < 13 && this.labelmd == 0){
42255 this.labelmd = this.labelWidth;
42258 if(this.labellg > 0){
42259 label.cls += ' col-lg-' + this.labellg;
42260 items.cls += ' col-lg-' + (12 - this.labellg);
42263 if(this.labelmd > 0){
42264 label.cls += ' col-md-' + this.labelmd;
42265 items.cls += ' col-md-' + (12 - this.labelmd);
42268 if(this.labelsm > 0){
42269 label.cls += ' col-sm-' + this.labelsm;
42270 items.cls += ' col-sm-' + (12 - this.labelsm);
42273 if(this.labelxs > 0){
42274 label.cls += ' col-xs-' + this.labelxs;
42275 items.cls += ' col-xs-' + (12 - this.labelxs);
42281 cls : 'roo-radio-set',
42285 cls : 'roo-radio-set-input',
42288 value : this.value ? this.value : ''
42295 if(this.weight.length){
42296 cfg.cls += ' roo-radio-' + this.weight;
42300 cfg.cls += ' roo-radio-set-inline';
42304 ['xs','sm','md','lg'].map(function(size){
42305 if (settings[size]) {
42306 cfg.cls += ' col-' + size + '-' + settings[size];
42314 initEvents : function()
42316 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
42317 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
42319 if(!this.fieldLabel.length){
42320 this.labelEl.hide();
42323 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
42324 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
42326 this.indicator = this.indicatorEl();
42328 if(this.indicator){
42329 this.indicator.addClass('invisible');
42332 this.originalValue = this.getValue();
42336 inputEl: function ()
42338 return this.el.select('.roo-radio-set-input', true).first();
42341 getChildContainer : function()
42343 return this.itemsEl;
42346 register : function(item)
42348 this.radioes.push(item);
42352 validate : function()
42354 if(this.getVisibilityEl().hasClass('hidden')){
42360 Roo.each(this.radioes, function(i){
42369 if(this.allowBlank) {
42373 if(this.disabled || valid){
42378 this.markInvalid();
42383 markValid : function()
42385 if(this.labelEl.isVisible(true) && this.indicatorEl()){
42386 this.indicatorEl().removeClass('visible');
42387 this.indicatorEl().addClass('invisible');
42391 if (Roo.bootstrap.version == 3) {
42392 this.el.removeClass([this.invalidClass, this.validClass]);
42393 this.el.addClass(this.validClass);
42395 this.el.removeClass(['is-invalid','is-valid']);
42396 this.el.addClass(['is-valid']);
42398 this.fireEvent('valid', this);
42401 markInvalid : function(msg)
42403 if(this.allowBlank || this.disabled){
42407 if(this.labelEl.isVisible(true) && this.indicatorEl()){
42408 this.indicatorEl().removeClass('invisible');
42409 this.indicatorEl().addClass('visible');
42411 if (Roo.bootstrap.version == 3) {
42412 this.el.removeClass([this.invalidClass, this.validClass]);
42413 this.el.addClass(this.invalidClass);
42415 this.el.removeClass(['is-invalid','is-valid']);
42416 this.el.addClass(['is-invalid']);
42419 this.fireEvent('invalid', this, msg);
42423 setValue : function(v, suppressEvent)
42425 if(this.value === v){
42432 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42435 Roo.each(this.radioes, function(i){
42437 i.el.removeClass('checked');
42440 Roo.each(this.radioes, function(i){
42442 if(i.value === v || i.value.toString() === v.toString()){
42444 i.el.addClass('checked');
42446 if(suppressEvent !== true){
42447 this.fireEvent('check', this, i);
42458 clearInvalid : function(){
42460 if(!this.el || this.preventMark){
42464 this.el.removeClass([this.invalidClass]);
42466 this.fireEvent('valid', this);
42471 Roo.apply(Roo.bootstrap.form.RadioSet, {
42475 register : function(set)
42477 this.groups[set.name] = set;
42480 get: function(name)
42482 if (typeof(this.groups[name]) == 'undefined') {
42486 return this.groups[name] ;
42492 * Ext JS Library 1.1.1
42493 * Copyright(c) 2006-2007, Ext JS, LLC.
42495 * Originally Released Under LGPL - original licence link has changed is not relivant.
42498 * <script type="text/javascript">
42503 * @class Roo.bootstrap.SplitBar
42504 * @extends Roo.util.Observable
42505 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
42509 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
42510 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
42511 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
42512 split.minSize = 100;
42513 split.maxSize = 600;
42514 split.animate = true;
42515 split.on('moved', splitterMoved);
42518 * Create a new SplitBar
42519 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
42520 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
42521 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42522 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
42523 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
42524 position of the SplitBar).
42526 Roo.bootstrap.SplitBar = function(cfg){
42531 // dragElement : elm
42532 // resizingElement: el,
42534 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
42535 // placement : Roo.bootstrap.SplitBar.LEFT ,
42536 // existingProxy ???
42539 this.el = Roo.get(cfg.dragElement, true);
42540 this.el.dom.unselectable = "on";
42542 this.resizingEl = Roo.get(cfg.resizingElement, true);
42546 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42547 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
42550 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
42553 * The minimum size of the resizing element. (Defaults to 0)
42559 * The maximum size of the resizing element. (Defaults to 2000)
42562 this.maxSize = 2000;
42565 * Whether to animate the transition to the new size
42568 this.animate = false;
42571 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
42574 this.useShim = false;
42579 if(!cfg.existingProxy){
42581 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
42583 this.proxy = Roo.get(cfg.existingProxy).dom;
42586 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
42589 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
42592 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
42595 this.dragSpecs = {};
42598 * @private The adapter to use to positon and resize elements
42600 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42601 this.adapter.init(this);
42603 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42605 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
42606 this.el.addClass("roo-splitbar-h");
42609 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
42610 this.el.addClass("roo-splitbar-v");
42616 * Fires when the splitter is moved (alias for {@link #event-moved})
42617 * @param {Roo.bootstrap.SplitBar} this
42618 * @param {Number} newSize the new width or height
42623 * Fires when the splitter is moved
42624 * @param {Roo.bootstrap.SplitBar} this
42625 * @param {Number} newSize the new width or height
42629 * @event beforeresize
42630 * Fires before the splitter is dragged
42631 * @param {Roo.bootstrap.SplitBar} this
42633 "beforeresize" : true,
42635 "beforeapply" : true
42638 Roo.util.Observable.call(this);
42641 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
42642 onStartProxyDrag : function(x, y){
42643 this.fireEvent("beforeresize", this);
42645 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
42647 o.enableDisplayMode("block");
42648 // all splitbars share the same overlay
42649 Roo.bootstrap.SplitBar.prototype.overlay = o;
42651 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
42652 this.overlay.show();
42653 Roo.get(this.proxy).setDisplayed("block");
42654 var size = this.adapter.getElementSize(this);
42655 this.activeMinSize = this.getMinimumSize();;
42656 this.activeMaxSize = this.getMaximumSize();;
42657 var c1 = size - this.activeMinSize;
42658 var c2 = Math.max(this.activeMaxSize - size, 0);
42659 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42660 this.dd.resetConstraints();
42661 this.dd.setXConstraint(
42662 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
42663 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
42665 this.dd.setYConstraint(0, 0);
42667 this.dd.resetConstraints();
42668 this.dd.setXConstraint(0, 0);
42669 this.dd.setYConstraint(
42670 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
42671 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
42674 this.dragSpecs.startSize = size;
42675 this.dragSpecs.startPoint = [x, y];
42676 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
42680 * @private Called after the drag operation by the DDProxy
42682 onEndProxyDrag : function(e){
42683 Roo.get(this.proxy).setDisplayed(false);
42684 var endPoint = Roo.lib.Event.getXY(e);
42686 this.overlay.hide();
42689 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42690 newSize = this.dragSpecs.startSize +
42691 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
42692 endPoint[0] - this.dragSpecs.startPoint[0] :
42693 this.dragSpecs.startPoint[0] - endPoint[0]
42696 newSize = this.dragSpecs.startSize +
42697 (this.placement == Roo.bootstrap.SplitBar.TOP ?
42698 endPoint[1] - this.dragSpecs.startPoint[1] :
42699 this.dragSpecs.startPoint[1] - endPoint[1]
42702 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
42703 if(newSize != this.dragSpecs.startSize){
42704 if(this.fireEvent('beforeapply', this, newSize) !== false){
42705 this.adapter.setElementSize(this, newSize);
42706 this.fireEvent("moved", this, newSize);
42707 this.fireEvent("resize", this, newSize);
42713 * Get the adapter this SplitBar uses
42714 * @return The adapter object
42716 getAdapter : function(){
42717 return this.adapter;
42721 * Set the adapter this SplitBar uses
42722 * @param {Object} adapter A SplitBar adapter object
42724 setAdapter : function(adapter){
42725 this.adapter = adapter;
42726 this.adapter.init(this);
42730 * Gets the minimum size for the resizing element
42731 * @return {Number} The minimum size
42733 getMinimumSize : function(){
42734 return this.minSize;
42738 * Sets the minimum size for the resizing element
42739 * @param {Number} minSize The minimum size
42741 setMinimumSize : function(minSize){
42742 this.minSize = minSize;
42746 * Gets the maximum size for the resizing element
42747 * @return {Number} The maximum size
42749 getMaximumSize : function(){
42750 return this.maxSize;
42754 * Sets the maximum size for the resizing element
42755 * @param {Number} maxSize The maximum size
42757 setMaximumSize : function(maxSize){
42758 this.maxSize = maxSize;
42762 * Sets the initialize size for the resizing element
42763 * @param {Number} size The initial size
42765 setCurrentSize : function(size){
42766 var oldAnimate = this.animate;
42767 this.animate = false;
42768 this.adapter.setElementSize(this, size);
42769 this.animate = oldAnimate;
42773 * Destroy this splitbar.
42774 * @param {Boolean} removeEl True to remove the element
42776 destroy : function(removeEl){
42778 this.shim.remove();
42781 this.proxy.parentNode.removeChild(this.proxy);
42789 * @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.
42791 Roo.bootstrap.SplitBar.createProxy = function(dir){
42792 var proxy = new Roo.Element(document.createElement("div"));
42793 proxy.unselectable();
42794 var cls = 'roo-splitbar-proxy';
42795 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
42796 document.body.appendChild(proxy.dom);
42801 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
42802 * Default Adapter. It assumes the splitter and resizing element are not positioned
42803 * elements and only gets/sets the width of the element. Generally used for table based layouts.
42805 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
42808 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
42809 // do nothing for now
42810 init : function(s){
42814 * Called before drag operations to get the current size of the resizing element.
42815 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42817 getElementSize : function(s){
42818 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42819 return s.resizingEl.getWidth();
42821 return s.resizingEl.getHeight();
42826 * Called after drag operations to set the size of the resizing element.
42827 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42828 * @param {Number} newSize The new size to set
42829 * @param {Function} onComplete A function to be invoked when resizing is complete
42831 setElementSize : function(s, newSize, onComplete){
42832 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42834 s.resizingEl.setWidth(newSize);
42836 onComplete(s, newSize);
42839 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
42844 s.resizingEl.setHeight(newSize);
42846 onComplete(s, newSize);
42849 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
42856 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
42857 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
42858 * Adapter that moves the splitter element to align with the resized sizing element.
42859 * Used with an absolute positioned SplitBar.
42860 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
42861 * document.body, make sure you assign an id to the body element.
42863 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
42864 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42865 this.container = Roo.get(container);
42868 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
42869 init : function(s){
42870 this.basic.init(s);
42873 getElementSize : function(s){
42874 return this.basic.getElementSize(s);
42877 setElementSize : function(s, newSize, onComplete){
42878 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
42881 moveSplitter : function(s){
42882 var yes = Roo.bootstrap.SplitBar;
42883 switch(s.placement){
42885 s.el.setX(s.resizingEl.getRight());
42888 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
42891 s.el.setY(s.resizingEl.getBottom());
42894 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
42901 * Orientation constant - Create a vertical SplitBar
42905 Roo.bootstrap.SplitBar.VERTICAL = 1;
42908 * Orientation constant - Create a horizontal SplitBar
42912 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
42915 * Placement constant - The resizing element is to the left of the splitter element
42919 Roo.bootstrap.SplitBar.LEFT = 1;
42922 * Placement constant - The resizing element is to the right of the splitter element
42926 Roo.bootstrap.SplitBar.RIGHT = 2;
42929 * Placement constant - The resizing element is positioned above the splitter element
42933 Roo.bootstrap.SplitBar.TOP = 3;
42936 * Placement constant - The resizing element is positioned under splitter element
42940 Roo.bootstrap.SplitBar.BOTTOM = 4;
42943 * Ext JS Library 1.1.1
42944 * Copyright(c) 2006-2007, Ext JS, LLC.
42946 * Originally Released Under LGPL - original licence link has changed is not relivant.
42949 * <script type="text/javascript">
42953 * @class Roo.bootstrap.layout.Manager
42954 * @extends Roo.bootstrap.Component
42956 * Base class for layout managers.
42958 Roo.bootstrap.layout.Manager = function(config)
42960 this.monitorWindowResize = true; // do this before we apply configuration.
42962 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42968 /** false to disable window resize monitoring @type Boolean */
42974 * Fires when a layout is performed.
42975 * @param {Roo.LayoutManager} this
42979 * @event regionresized
42980 * Fires when the user resizes a region.
42981 * @param {Roo.LayoutRegion} region The resized region
42982 * @param {Number} newSize The new size (width for east/west, height for north/south)
42984 "regionresized" : true,
42986 * @event regioncollapsed
42987 * Fires when a region is collapsed.
42988 * @param {Roo.LayoutRegion} region The collapsed region
42990 "regioncollapsed" : true,
42992 * @event regionexpanded
42993 * Fires when a region is expanded.
42994 * @param {Roo.LayoutRegion} region The expanded region
42996 "regionexpanded" : true
42998 this.updating = false;
43001 this.el = Roo.get(config.el);
43007 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
43012 monitorWindowResize : true,
43018 onRender : function(ct, position)
43021 this.el = Roo.get(ct);
43024 //this.fireEvent('render',this);
43028 initEvents: function()
43032 // ie scrollbar fix
43033 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
43034 document.body.scroll = "no";
43035 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
43036 this.el.position('relative');
43038 this.id = this.el.id;
43039 this.el.addClass("roo-layout-container");
43040 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
43041 if(this.el.dom != document.body ) {
43042 this.el.on('resize', this.layout,this);
43043 this.el.on('show', this.layout,this);
43049 * Returns true if this layout is currently being updated
43050 * @return {Boolean}
43052 isUpdating : function(){
43053 return this.updating;
43057 * Suspend the LayoutManager from doing auto-layouts while
43058 * making multiple add or remove calls
43060 beginUpdate : function(){
43061 this.updating = true;
43065 * Restore auto-layouts and optionally disable the manager from performing a layout
43066 * @param {Boolean} noLayout true to disable a layout update
43068 endUpdate : function(noLayout){
43069 this.updating = false;
43075 layout: function(){
43079 onRegionResized : function(region, newSize){
43080 this.fireEvent("regionresized", region, newSize);
43084 onRegionCollapsed : function(region){
43085 this.fireEvent("regioncollapsed", region);
43088 onRegionExpanded : function(region){
43089 this.fireEvent("regionexpanded", region);
43093 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
43094 * performs box-model adjustments.
43095 * @return {Object} The size as an object {width: (the width), height: (the height)}
43097 getViewSize : function()
43100 if(this.el.dom != document.body){
43101 size = this.el.getSize();
43103 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
43105 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
43106 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
43111 * Returns the Element this layout is bound to.
43112 * @return {Roo.Element}
43114 getEl : function(){
43119 * Returns the specified region.
43120 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
43121 * @return {Roo.LayoutRegion}
43123 getRegion : function(target){
43124 return this.regions[target.toLowerCase()];
43127 onWindowResize : function(){
43128 if(this.monitorWindowResize){
43135 * Ext JS Library 1.1.1
43136 * Copyright(c) 2006-2007, Ext JS, LLC.
43138 * Originally Released Under LGPL - original licence link has changed is not relivant.
43141 * <script type="text/javascript">
43144 * @class Roo.bootstrap.layout.Border
43145 * @extends Roo.bootstrap.layout.Manager
43146 * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
43147 * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
43148 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
43149 * please see: examples/bootstrap/nested.html<br><br>
43151 <b>The container the layout is rendered into can be either the body element or any other element.
43152 If it is not the body element, the container needs to either be an absolute positioned element,
43153 or you will need to add "position:relative" to the css of the container. You will also need to specify
43154 the container size if it is not the body element.</b>
43157 * Create a new Border
43158 * @param {Object} config Configuration options
43160 Roo.bootstrap.layout.Border = function(config){
43161 config = config || {};
43162 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
43166 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
43167 if(config[region]){
43168 config[region].region = region;
43169 this.addRegion(config[region]);
43175 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
43177 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
43180 * @cfg {Roo.bootstrap.layout.Region} center region to go in center
43183 * @cfg {Roo.bootstrap.layout.Region} west region to go in west
43186 * @cfg {Roo.bootstrap.layout.Region} east region to go in east
43189 * @cfg {Roo.bootstrap.layout.Region} south region to go in south
43192 * @cfg {Roo.bootstrap.layout.Region} north region to go in north
43198 parent : false, // this might point to a 'nest' or a ???
43201 * Creates and adds a new region if it doesn't already exist.
43202 * @param {String} target The target region key (north, south, east, west or center).
43203 * @param {Object} config The regions config object
43204 * @return {BorderLayoutRegion} The new region
43206 addRegion : function(config)
43208 if(!this.regions[config.region]){
43209 var r = this.factory(config);
43210 this.bindRegion(r);
43212 return this.regions[config.region];
43216 bindRegion : function(r){
43217 this.regions[r.config.region] = r;
43219 r.on("visibilitychange", this.layout, this);
43220 r.on("paneladded", this.layout, this);
43221 r.on("panelremoved", this.layout, this);
43222 r.on("invalidated", this.layout, this);
43223 r.on("resized", this.onRegionResized, this);
43224 r.on("collapsed", this.onRegionCollapsed, this);
43225 r.on("expanded", this.onRegionExpanded, this);
43229 * Performs a layout update.
43231 layout : function()
43233 if(this.updating) {
43237 // render all the rebions if they have not been done alreayd?
43238 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
43239 if(this.regions[region] && !this.regions[region].bodyEl){
43240 this.regions[region].onRender(this.el)
43244 var size = this.getViewSize();
43245 var w = size.width;
43246 var h = size.height;
43251 //var x = 0, y = 0;
43253 var rs = this.regions;
43254 var north = rs["north"];
43255 var south = rs["south"];
43256 var west = rs["west"];
43257 var east = rs["east"];
43258 var center = rs["center"];
43259 //if(this.hideOnLayout){ // not supported anymore
43260 //c.el.setStyle("display", "none");
43262 if(north && north.isVisible()){
43263 var b = north.getBox();
43264 var m = north.getMargins();
43265 b.width = w - (m.left+m.right);
43268 centerY = b.height + b.y + m.bottom;
43269 centerH -= centerY;
43270 north.updateBox(this.safeBox(b));
43272 if(south && south.isVisible()){
43273 var b = south.getBox();
43274 var m = south.getMargins();
43275 b.width = w - (m.left+m.right);
43277 var totalHeight = (b.height + m.top + m.bottom);
43278 b.y = h - totalHeight + m.top;
43279 centerH -= totalHeight;
43280 south.updateBox(this.safeBox(b));
43282 if(west && west.isVisible()){
43283 var b = west.getBox();
43284 var m = west.getMargins();
43285 b.height = centerH - (m.top+m.bottom);
43287 b.y = centerY + m.top;
43288 var totalWidth = (b.width + m.left + m.right);
43289 centerX += totalWidth;
43290 centerW -= totalWidth;
43291 west.updateBox(this.safeBox(b));
43293 if(east && east.isVisible()){
43294 var b = east.getBox();
43295 var m = east.getMargins();
43296 b.height = centerH - (m.top+m.bottom);
43297 var totalWidth = (b.width + m.left + m.right);
43298 b.x = w - totalWidth + m.left;
43299 b.y = centerY + m.top;
43300 centerW -= totalWidth;
43301 east.updateBox(this.safeBox(b));
43304 var m = center.getMargins();
43306 x: centerX + m.left,
43307 y: centerY + m.top,
43308 width: centerW - (m.left+m.right),
43309 height: centerH - (m.top+m.bottom)
43311 //if(this.hideOnLayout){
43312 //center.el.setStyle("display", "block");
43314 center.updateBox(this.safeBox(centerBox));
43317 this.fireEvent("layout", this);
43321 safeBox : function(box){
43322 box.width = Math.max(0, box.width);
43323 box.height = Math.max(0, box.height);
43328 * Adds a ContentPanel (or subclass) to this layout.
43329 * @param {String} target The target region key (north, south, east, west or center).
43330 * @param {Roo.ContentPanel} panel The panel to add
43331 * @return {Roo.ContentPanel} The added panel
43333 add : function(target, panel){
43335 target = target.toLowerCase();
43336 return this.regions[target].add(panel);
43340 * Remove a ContentPanel (or subclass) to this layout.
43341 * @param {String} target The target region key (north, south, east, west or center).
43342 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
43343 * @return {Roo.ContentPanel} The removed panel
43345 remove : function(target, panel){
43346 target = target.toLowerCase();
43347 return this.regions[target].remove(panel);
43351 * Searches all regions for a panel with the specified id
43352 * @param {String} panelId
43353 * @return {Roo.ContentPanel} The panel or null if it wasn't found
43355 findPanel : function(panelId){
43356 var rs = this.regions;
43357 for(var target in rs){
43358 if(typeof rs[target] != "function"){
43359 var p = rs[target].getPanel(panelId);
43369 * Searches all regions for a panel with the specified id and activates (shows) it.
43370 * @param {String/ContentPanel} panelId The panels id or the panel itself
43371 * @return {Roo.ContentPanel} The shown panel or null
43373 showPanel : function(panelId) {
43374 var rs = this.regions;
43375 for(var target in rs){
43376 var r = rs[target];
43377 if(typeof r != "function"){
43378 if(r.hasPanel(panelId)){
43379 return r.showPanel(panelId);
43387 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43388 * @param {Roo.state.Provider} provider (optional) An alternate state provider
43391 restoreState : function(provider){
43393 provider = Roo.state.Manager;
43395 var sm = new Roo.LayoutStateManager();
43396 sm.init(this, provider);
43402 * Adds a xtype elements to the layout.
43406 xtype : 'ContentPanel',
43413 xtype : 'NestedLayoutPanel',
43419 items : [ ... list of content panels or nested layout panels.. ]
43423 * @param {Object} cfg Xtype definition of item to add.
43425 addxtype : function(cfg)
43427 // basically accepts a pannel...
43428 // can accept a layout region..!?!?
43429 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
43432 // theory? children can only be panels??
43434 //if (!cfg.xtype.match(/Panel$/)) {
43439 if (typeof(cfg.region) == 'undefined') {
43440 Roo.log("Failed to add Panel, region was not set");
43444 var region = cfg.region;
43450 xitems = cfg.items;
43455 if ( region == 'center') {
43456 Roo.log("Center: " + cfg.title);
43462 case 'Content': // ContentPanel (el, cfg)
43463 case 'Scroll': // ContentPanel (el, cfg)
43465 cfg.autoCreate = cfg.autoCreate || true;
43466 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43468 // var el = this.el.createChild();
43469 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43472 this.add(region, ret);
43476 case 'TreePanel': // our new panel!
43477 cfg.el = this.el.createChild();
43478 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43479 this.add(region, ret);
43484 // create a new Layout (which is a Border Layout...
43486 var clayout = cfg.layout;
43487 clayout.el = this.el.createChild();
43488 clayout.items = clayout.items || [];
43492 // replace this exitems with the clayout ones..
43493 xitems = clayout.items;
43495 // force background off if it's in center...
43496 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43497 cfg.background = false;
43499 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
43502 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43503 //console.log('adding nested layout panel ' + cfg.toSource());
43504 this.add(region, ret);
43505 nb = {}; /// find first...
43510 // needs grid and region
43512 //var el = this.getRegion(region).el.createChild();
43514 *var el = this.el.createChild();
43515 // create the grid first...
43516 cfg.grid.container = el;
43517 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
43520 if (region == 'center' && this.active ) {
43521 cfg.background = false;
43524 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43526 this.add(region, ret);
43528 if (cfg.background) {
43529 // render grid on panel activation (if panel background)
43530 ret.on('activate', function(gp) {
43531 if (!gp.grid.rendered) {
43532 // gp.grid.render(el);
43536 // cfg.grid.render(el);
43542 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
43543 // it was the old xcomponent building that caused this before.
43544 // espeically if border is the top element in the tree.
43554 if (typeof(Roo[cfg.xtype]) != 'undefined') {
43556 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43557 this.add(region, ret);
43561 throw "Can not add '" + cfg.xtype + "' to Border";
43567 this.beginUpdate();
43571 Roo.each(xitems, function(i) {
43572 region = nb && i.region ? i.region : false;
43574 var add = ret.addxtype(i);
43577 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
43578 if (!i.background) {
43579 abn[region] = nb[region] ;
43586 // make the last non-background panel active..
43587 //if (nb) { Roo.log(abn); }
43590 for(var r in abn) {
43591 region = this.getRegion(r);
43593 // tried using nb[r], but it does not work..
43595 region.showPanel(abn[r]);
43606 factory : function(cfg)
43609 var validRegions = Roo.bootstrap.layout.Border.regions;
43611 var target = cfg.region;
43614 var r = Roo.bootstrap.layout;
43618 return new r.North(cfg);
43620 return new r.South(cfg);
43622 return new r.East(cfg);
43624 return new r.West(cfg);
43626 return new r.Center(cfg);
43628 throw 'Layout region "'+target+'" not supported.';
43635 * Ext JS Library 1.1.1
43636 * Copyright(c) 2006-2007, Ext JS, LLC.
43638 * Originally Released Under LGPL - original licence link has changed is not relivant.
43641 * <script type="text/javascript">
43645 * @class Roo.bootstrap.layout.Basic
43646 * @extends Roo.util.Observable
43647 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
43648 * and does not have a titlebar, tabs or any other features. All it does is size and position
43649 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
43650 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
43651 * @cfg {string} region the region that it inhabits..
43652 * @cfg {bool} skipConfig skip config?
43656 Roo.bootstrap.layout.Basic = function(config){
43658 this.mgr = config.mgr;
43660 this.position = config.region;
43662 var skipConfig = config.skipConfig;
43666 * @scope Roo.BasicLayoutRegion
43670 * @event beforeremove
43671 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
43672 * @param {Roo.LayoutRegion} this
43673 * @param {Roo.ContentPanel} panel The panel
43674 * @param {Object} e The cancel event object
43676 "beforeremove" : true,
43678 * @event invalidated
43679 * Fires when the layout for this region is changed.
43680 * @param {Roo.LayoutRegion} this
43682 "invalidated" : true,
43684 * @event visibilitychange
43685 * Fires when this region is shown or hidden
43686 * @param {Roo.LayoutRegion} this
43687 * @param {Boolean} visibility true or false
43689 "visibilitychange" : true,
43691 * @event paneladded
43692 * Fires when a panel is added.
43693 * @param {Roo.LayoutRegion} this
43694 * @param {Roo.ContentPanel} panel The panel
43696 "paneladded" : true,
43698 * @event panelremoved
43699 * Fires when a panel is removed.
43700 * @param {Roo.LayoutRegion} this
43701 * @param {Roo.ContentPanel} panel The panel
43703 "panelremoved" : true,
43705 * @event beforecollapse
43706 * Fires when this region before collapse.
43707 * @param {Roo.LayoutRegion} this
43709 "beforecollapse" : true,
43712 * Fires when this region is collapsed.
43713 * @param {Roo.LayoutRegion} this
43715 "collapsed" : true,
43718 * Fires when this region is expanded.
43719 * @param {Roo.LayoutRegion} this
43724 * Fires when this region is slid into view.
43725 * @param {Roo.LayoutRegion} this
43727 "slideshow" : true,
43730 * Fires when this region slides out of view.
43731 * @param {Roo.LayoutRegion} this
43733 "slidehide" : true,
43735 * @event panelactivated
43736 * Fires when a panel is activated.
43737 * @param {Roo.LayoutRegion} this
43738 * @param {Roo.ContentPanel} panel The activated panel
43740 "panelactivated" : true,
43743 * Fires when the user resizes this region.
43744 * @param {Roo.LayoutRegion} this
43745 * @param {Number} newSize The new size (width for east/west, height for north/south)
43749 /** A collection of panels in this region. @type Roo.util.MixedCollection */
43750 this.panels = new Roo.util.MixedCollection();
43751 this.panels.getKey = this.getPanelId.createDelegate(this);
43753 this.activePanel = null;
43754 // ensure listeners are added...
43756 if (config.listeners || config.events) {
43757 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
43758 listeners : config.listeners || {},
43759 events : config.events || {}
43763 if(skipConfig !== true){
43764 this.applyConfig(config);
43768 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
43770 getPanelId : function(p){
43774 applyConfig : function(config){
43775 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43776 this.config = config;
43781 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
43782 * the width, for horizontal (north, south) the height.
43783 * @param {Number} newSize The new width or height
43785 resizeTo : function(newSize){
43786 var el = this.el ? this.el :
43787 (this.activePanel ? this.activePanel.getEl() : null);
43789 switch(this.position){
43792 el.setWidth(newSize);
43793 this.fireEvent("resized", this, newSize);
43797 el.setHeight(newSize);
43798 this.fireEvent("resized", this, newSize);
43804 getBox : function(){
43805 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43808 getMargins : function(){
43809 return this.margins;
43812 updateBox : function(box){
43814 var el = this.activePanel.getEl();
43815 el.dom.style.left = box.x + "px";
43816 el.dom.style.top = box.y + "px";
43817 this.activePanel.setSize(box.width, box.height);
43821 * Returns the container element for this region.
43822 * @return {Roo.Element}
43824 getEl : function(){
43825 return this.activePanel;
43829 * Returns true if this region is currently visible.
43830 * @return {Boolean}
43832 isVisible : function(){
43833 return this.activePanel ? true : false;
43836 setActivePanel : function(panel){
43837 panel = this.getPanel(panel);
43838 if(this.activePanel && this.activePanel != panel){
43839 this.activePanel.setActiveState(false);
43840 this.activePanel.getEl().setLeftTop(-10000,-10000);
43842 this.activePanel = panel;
43843 panel.setActiveState(true);
43845 panel.setSize(this.box.width, this.box.height);
43847 this.fireEvent("panelactivated", this, panel);
43848 this.fireEvent("invalidated");
43852 * Show the specified panel.
43853 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43854 * @return {Roo.ContentPanel} The shown panel or null
43856 showPanel : function(panel){
43857 panel = this.getPanel(panel);
43859 this.setActivePanel(panel);
43865 * Get the active panel for this region.
43866 * @return {Roo.ContentPanel} The active panel or null
43868 getActivePanel : function(){
43869 return this.activePanel;
43873 * Add the passed ContentPanel(s)
43874 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43875 * @return {Roo.ContentPanel} The panel added (if only one was added)
43877 add : function(panel){
43878 if(arguments.length > 1){
43879 for(var i = 0, len = arguments.length; i < len; i++) {
43880 this.add(arguments[i]);
43884 if(this.hasPanel(panel)){
43885 this.showPanel(panel);
43888 var el = panel.getEl();
43889 if(el.dom.parentNode != this.mgr.el.dom){
43890 this.mgr.el.dom.appendChild(el.dom);
43892 if(panel.setRegion){
43893 panel.setRegion(this);
43895 this.panels.add(panel);
43896 el.setStyle("position", "absolute");
43897 if(!panel.background){
43898 this.setActivePanel(panel);
43899 if(this.config.initialSize && this.panels.getCount()==1){
43900 this.resizeTo(this.config.initialSize);
43903 this.fireEvent("paneladded", this, panel);
43908 * Returns true if the panel is in this region.
43909 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43910 * @return {Boolean}
43912 hasPanel : function(panel){
43913 if(typeof panel == "object"){ // must be panel obj
43914 panel = panel.getId();
43916 return this.getPanel(panel) ? true : false;
43920 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43921 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43922 * @param {Boolean} preservePanel Overrides the config preservePanel option
43923 * @return {Roo.ContentPanel} The panel that was removed
43925 remove : function(panel, preservePanel){
43926 panel = this.getPanel(panel);
43931 this.fireEvent("beforeremove", this, panel, e);
43932 if(e.cancel === true){
43935 var panelId = panel.getId();
43936 this.panels.removeKey(panelId);
43941 * Returns the panel specified or null if it's not in this region.
43942 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43943 * @return {Roo.ContentPanel}
43945 getPanel : function(id){
43946 if(typeof id == "object"){ // must be panel obj
43949 return this.panels.get(id);
43953 * Returns this regions position (north/south/east/west/center).
43956 getPosition: function(){
43957 return this.position;
43961 * Ext JS Library 1.1.1
43962 * Copyright(c) 2006-2007, Ext JS, LLC.
43964 * Originally Released Under LGPL - original licence link has changed is not relivant.
43967 * <script type="text/javascript">
43971 * @class Roo.bootstrap.layout.Region
43972 * @extends Roo.bootstrap.layout.Basic
43973 * This class represents a region in a layout manager.
43975 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43976 * @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})
43977 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
43978 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
43979 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
43980 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
43981 * @cfg {String} title The title for the region (overrides panel titles)
43982 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
43983 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43984 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
43985 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43986 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
43987 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43988 * the space available, similar to FireFox 1.5 tabs (defaults to false)
43989 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
43990 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
43991 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
43993 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
43994 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
43995 * @cfg {Boolean} disableTabTips True to disable tab tooltips
43996 * @cfg {Number} width For East/West panels
43997 * @cfg {Number} height For North/South panels
43998 * @cfg {Boolean} split To show the splitter
43999 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
44001 * @cfg {string} cls Extra CSS classes to add to region
44003 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
44004 * @cfg {string} region the region that it inhabits..
44007 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
44008 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
44010 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
44011 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
44012 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
44014 Roo.bootstrap.layout.Region = function(config)
44016 this.applyConfig(config);
44018 var mgr = config.mgr;
44019 var pos = config.region;
44020 config.skipConfig = true;
44021 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
44024 this.onRender(mgr.el);
44027 this.visible = true;
44028 this.collapsed = false;
44029 this.unrendered_panels = [];
44032 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
44034 position: '', // set by wrapper (eg. north/south etc..)
44035 unrendered_panels : null, // unrendered panels.
44037 tabPosition : false,
44039 mgr: false, // points to 'Border'
44042 createBody : function(){
44043 /** This region's body element
44044 * @type Roo.Element */
44045 this.bodyEl = this.el.createChild({
44047 cls: "roo-layout-panel-body tab-content" // bootstrap added...
44051 onRender: function(ctr, pos)
44053 var dh = Roo.DomHelper;
44054 /** This region's container element
44055 * @type Roo.Element */
44056 this.el = dh.append(ctr.dom, {
44058 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
44060 /** This region's title element
44061 * @type Roo.Element */
44063 this.titleEl = dh.append(this.el.dom, {
44065 unselectable: "on",
44066 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
44068 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
44069 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
44073 this.titleEl.enableDisplayMode();
44074 /** This region's title text element
44075 * @type HTMLElement */
44076 this.titleTextEl = this.titleEl.dom.firstChild;
44077 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
44079 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
44080 this.closeBtn.enableDisplayMode();
44081 this.closeBtn.on("click", this.closeClicked, this);
44082 this.closeBtn.hide();
44084 this.createBody(this.config);
44085 if(this.config.hideWhenEmpty){
44087 this.on("paneladded", this.validateVisibility, this);
44088 this.on("panelremoved", this.validateVisibility, this);
44090 if(this.autoScroll){
44091 this.bodyEl.setStyle("overflow", "auto");
44093 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
44095 //if(c.titlebar !== false){
44096 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
44097 this.titleEl.hide();
44099 this.titleEl.show();
44100 if(this.config.title){
44101 this.titleTextEl.innerHTML = this.config.title;
44105 if(this.config.collapsed){
44106 this.collapse(true);
44108 if(this.config.hidden){
44112 if (this.unrendered_panels && this.unrendered_panels.length) {
44113 for (var i =0;i< this.unrendered_panels.length; i++) {
44114 this.add(this.unrendered_panels[i]);
44116 this.unrendered_panels = null;
44122 applyConfig : function(c)
44125 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
44126 var dh = Roo.DomHelper;
44127 if(c.titlebar !== false){
44128 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
44129 this.collapseBtn.on("click", this.collapse, this);
44130 this.collapseBtn.enableDisplayMode();
44132 if(c.showPin === true || this.showPin){
44133 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
44134 this.stickBtn.enableDisplayMode();
44135 this.stickBtn.on("click", this.expand, this);
44136 this.stickBtn.hide();
44141 /** This region's collapsed element
44142 * @type Roo.Element */
44145 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
44146 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
44149 if(c.floatable !== false){
44150 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
44151 this.collapsedEl.on("click", this.collapseClick, this);
44154 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
44155 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
44156 id: "message", unselectable: "on", style:{"float":"left"}});
44157 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
44159 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
44160 this.expandBtn.on("click", this.expand, this);
44164 if(this.collapseBtn){
44165 this.collapseBtn.setVisible(c.collapsible == true);
44168 this.cmargins = c.cmargins || this.cmargins ||
44169 (this.position == "west" || this.position == "east" ?
44170 {top: 0, left: 2, right:2, bottom: 0} :
44171 {top: 2, left: 0, right:0, bottom: 2});
44173 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44176 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
44178 this.autoScroll = c.autoScroll || false;
44183 this.duration = c.duration || .30;
44184 this.slideDuration = c.slideDuration || .45;
44189 * Returns true if this region is currently visible.
44190 * @return {Boolean}
44192 isVisible : function(){
44193 return this.visible;
44197 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
44198 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
44200 //setCollapsedTitle : function(title){
44201 // title = title || " ";
44202 // if(this.collapsedTitleTextEl){
44203 // this.collapsedTitleTextEl.innerHTML = title;
44207 getBox : function(){
44209 // if(!this.collapsed){
44210 b = this.el.getBox(false, true);
44212 // b = this.collapsedEl.getBox(false, true);
44217 getMargins : function(){
44218 return this.margins;
44219 //return this.collapsed ? this.cmargins : this.margins;
44222 highlight : function(){
44223 this.el.addClass("x-layout-panel-dragover");
44226 unhighlight : function(){
44227 this.el.removeClass("x-layout-panel-dragover");
44230 updateBox : function(box)
44232 if (!this.bodyEl) {
44233 return; // not rendered yet..
44237 if(!this.collapsed){
44238 this.el.dom.style.left = box.x + "px";
44239 this.el.dom.style.top = box.y + "px";
44240 this.updateBody(box.width, box.height);
44242 this.collapsedEl.dom.style.left = box.x + "px";
44243 this.collapsedEl.dom.style.top = box.y + "px";
44244 this.collapsedEl.setSize(box.width, box.height);
44247 this.tabs.autoSizeTabs();
44251 updateBody : function(w, h)
44254 this.el.setWidth(w);
44255 w -= this.el.getBorderWidth("rl");
44256 if(this.config.adjustments){
44257 w += this.config.adjustments[0];
44260 if(h !== null && h > 0){
44261 this.el.setHeight(h);
44262 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
44263 h -= this.el.getBorderWidth("tb");
44264 if(this.config.adjustments){
44265 h += this.config.adjustments[1];
44267 this.bodyEl.setHeight(h);
44269 h = this.tabs.syncHeight(h);
44272 if(this.panelSize){
44273 w = w !== null ? w : this.panelSize.width;
44274 h = h !== null ? h : this.panelSize.height;
44276 if(this.activePanel){
44277 var el = this.activePanel.getEl();
44278 w = w !== null ? w : el.getWidth();
44279 h = h !== null ? h : el.getHeight();
44280 this.panelSize = {width: w, height: h};
44281 this.activePanel.setSize(w, h);
44283 if(Roo.isIE && this.tabs){
44284 this.tabs.el.repaint();
44289 * Returns the container element for this region.
44290 * @return {Roo.Element}
44292 getEl : function(){
44297 * Hides this region.
44300 //if(!this.collapsed){
44301 this.el.dom.style.left = "-2000px";
44304 // this.collapsedEl.dom.style.left = "-2000px";
44305 // this.collapsedEl.hide();
44307 this.visible = false;
44308 this.fireEvent("visibilitychange", this, false);
44312 * Shows this region if it was previously hidden.
44315 //if(!this.collapsed){
44318 // this.collapsedEl.show();
44320 this.visible = true;
44321 this.fireEvent("visibilitychange", this, true);
44324 closeClicked : function(){
44325 if(this.activePanel){
44326 this.remove(this.activePanel);
44330 collapseClick : function(e){
44332 e.stopPropagation();
44335 e.stopPropagation();
44341 * Collapses this region.
44342 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
44345 collapse : function(skipAnim, skipCheck = false){
44346 if(this.collapsed) {
44350 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
44352 this.collapsed = true;
44354 this.split.el.hide();
44356 if(this.config.animate && skipAnim !== true){
44357 this.fireEvent("invalidated", this);
44358 this.animateCollapse();
44360 this.el.setLocation(-20000,-20000);
44362 this.collapsedEl.show();
44363 this.fireEvent("collapsed", this);
44364 this.fireEvent("invalidated", this);
44370 animateCollapse : function(){
44375 * Expands this region if it was previously collapsed.
44376 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44377 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44380 expand : function(e, skipAnim){
44382 e.stopPropagation();
44384 if(!this.collapsed || this.el.hasActiveFx()) {
44388 this.afterSlideIn();
44391 this.collapsed = false;
44392 if(this.config.animate && skipAnim !== true){
44393 this.animateExpand();
44397 this.split.el.show();
44399 this.collapsedEl.setLocation(-2000,-2000);
44400 this.collapsedEl.hide();
44401 this.fireEvent("invalidated", this);
44402 this.fireEvent("expanded", this);
44406 animateExpand : function(){
44410 initTabs : function()
44412 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
44414 var ts = new Roo.bootstrap.panel.Tabs({
44415 el: this.bodyEl.dom,
44417 tabPosition: this.tabPosition ? this.tabPosition : 'top',
44418 disableTooltips: this.config.disableTabTips,
44419 toolbar : this.config.toolbar
44422 if(this.config.hideTabs){
44423 ts.stripWrap.setDisplayed(false);
44426 ts.resizeTabs = this.config.resizeTabs === true;
44427 ts.minTabWidth = this.config.minTabWidth || 40;
44428 ts.maxTabWidth = this.config.maxTabWidth || 250;
44429 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44430 ts.monitorResize = false;
44431 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
44432 ts.bodyEl.addClass('roo-layout-tabs-body');
44433 this.panels.each(this.initPanelAsTab, this);
44436 initPanelAsTab : function(panel){
44437 var ti = this.tabs.addTab(
44441 this.config.closeOnTab && panel.isClosable(),
44444 if(panel.tabTip !== undefined){
44445 ti.setTooltip(panel.tabTip);
44447 ti.on("activate", function(){
44448 this.setActivePanel(panel);
44451 if(this.config.closeOnTab){
44452 ti.on("beforeclose", function(t, e){
44454 this.remove(panel);
44458 panel.tabItem = ti;
44463 updatePanelTitle : function(panel, title)
44465 if(this.activePanel == panel){
44466 this.updateTitle(title);
44469 var ti = this.tabs.getTab(panel.getEl().id);
44471 if(panel.tabTip !== undefined){
44472 ti.setTooltip(panel.tabTip);
44477 updateTitle : function(title){
44478 if(this.titleTextEl && !this.config.title){
44479 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
44483 setActivePanel : function(panel)
44485 panel = this.getPanel(panel);
44486 if(this.activePanel && this.activePanel != panel){
44487 if(this.activePanel.setActiveState(false) === false){
44491 this.activePanel = panel;
44492 panel.setActiveState(true);
44493 if(this.panelSize){
44494 panel.setSize(this.panelSize.width, this.panelSize.height);
44497 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44499 this.updateTitle(panel.getTitle());
44501 this.fireEvent("invalidated", this);
44503 this.fireEvent("panelactivated", this, panel);
44507 * Shows the specified panel.
44508 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44509 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44511 showPanel : function(panel)
44513 panel = this.getPanel(panel);
44516 var tab = this.tabs.getTab(panel.getEl().id);
44517 if(tab.isHidden()){
44518 this.tabs.unhideTab(tab.id);
44522 this.setActivePanel(panel);
44529 * Get the active panel for this region.
44530 * @return {Roo.ContentPanel} The active panel or null
44532 getActivePanel : function(){
44533 return this.activePanel;
44536 validateVisibility : function(){
44537 if(this.panels.getCount() < 1){
44538 this.updateTitle(" ");
44539 this.closeBtn.hide();
44542 if(!this.isVisible()){
44549 * Adds the passed ContentPanel(s) to this region.
44550 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44551 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44553 add : function(panel)
44555 if(arguments.length > 1){
44556 for(var i = 0, len = arguments.length; i < len; i++) {
44557 this.add(arguments[i]);
44562 // if we have not been rendered yet, then we can not really do much of this..
44563 if (!this.bodyEl) {
44564 this.unrendered_panels.push(panel);
44571 if(this.hasPanel(panel)){
44572 this.showPanel(panel);
44575 panel.setRegion(this);
44576 this.panels.add(panel);
44577 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44578 // sinle panel - no tab...?? would it not be better to render it with the tabs,
44579 // and hide them... ???
44580 this.bodyEl.dom.appendChild(panel.getEl().dom);
44581 if(panel.background !== true){
44582 this.setActivePanel(panel);
44584 this.fireEvent("paneladded", this, panel);
44591 this.initPanelAsTab(panel);
44595 if(panel.background !== true){
44596 this.tabs.activate(panel.getEl().id);
44598 this.fireEvent("paneladded", this, panel);
44603 * Hides the tab for the specified panel.
44604 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44606 hidePanel : function(panel){
44607 if(this.tabs && (panel = this.getPanel(panel))){
44608 this.tabs.hideTab(panel.getEl().id);
44613 * Unhides the tab for a previously hidden panel.
44614 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44616 unhidePanel : function(panel){
44617 if(this.tabs && (panel = this.getPanel(panel))){
44618 this.tabs.unhideTab(panel.getEl().id);
44622 clearPanels : function(){
44623 while(this.panels.getCount() > 0){
44624 this.remove(this.panels.first());
44629 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44630 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44631 * @param {Boolean} preservePanel Overrides the config preservePanel option
44632 * @return {Roo.ContentPanel} The panel that was removed
44634 remove : function(panel, preservePanel)
44636 panel = this.getPanel(panel);
44641 this.fireEvent("beforeremove", this, panel, e);
44642 if(e.cancel === true){
44645 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44646 var panelId = panel.getId();
44647 this.panels.removeKey(panelId);
44649 document.body.appendChild(panel.getEl().dom);
44652 this.tabs.removeTab(panel.getEl().id);
44653 }else if (!preservePanel){
44654 this.bodyEl.dom.removeChild(panel.getEl().dom);
44656 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44657 var p = this.panels.first();
44658 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44659 tempEl.appendChild(p.getEl().dom);
44660 this.bodyEl.update("");
44661 this.bodyEl.dom.appendChild(p.getEl().dom);
44663 this.updateTitle(p.getTitle());
44665 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44666 this.setActivePanel(p);
44668 panel.setRegion(null);
44669 if(this.activePanel == panel){
44670 this.activePanel = null;
44672 if(this.config.autoDestroy !== false && preservePanel !== true){
44673 try{panel.destroy();}catch(e){}
44675 this.fireEvent("panelremoved", this, panel);
44680 * Returns the TabPanel component used by this region
44681 * @return {Roo.TabPanel}
44683 getTabs : function(){
44687 createTool : function(parentEl, className){
44688 var btn = Roo.DomHelper.append(parentEl, {
44690 cls: "x-layout-tools-button",
44693 cls: "roo-layout-tools-button-inner " + className,
44697 btn.addClassOnOver("roo-layout-tools-button-over");
44702 * Ext JS Library 1.1.1
44703 * Copyright(c) 2006-2007, Ext JS, LLC.
44705 * Originally Released Under LGPL - original licence link has changed is not relivant.
44708 * <script type="text/javascript">
44714 * @class Roo.SplitLayoutRegion
44715 * @extends Roo.LayoutRegion
44716 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44718 Roo.bootstrap.layout.Split = function(config){
44719 this.cursor = config.cursor;
44720 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
44723 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
44725 splitTip : "Drag to resize.",
44726 collapsibleSplitTip : "Drag to resize. Double click to hide.",
44727 useSplitTips : false,
44729 applyConfig : function(config){
44730 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
44733 onRender : function(ctr,pos) {
44735 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
44736 if(!this.config.split){
44741 var splitEl = Roo.DomHelper.append(ctr.dom, {
44743 id: this.el.id + "-split",
44744 cls: "roo-layout-split roo-layout-split-"+this.position,
44747 /** The SplitBar for this region
44748 * @type Roo.SplitBar */
44749 // does not exist yet...
44750 Roo.log([this.position, this.orientation]);
44752 this.split = new Roo.bootstrap.SplitBar({
44753 dragElement : splitEl,
44754 resizingElement: this.el,
44755 orientation : this.orientation
44758 this.split.on("moved", this.onSplitMove, this);
44759 this.split.useShim = this.config.useShim === true;
44760 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44761 if(this.useSplitTips){
44762 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44764 //if(config.collapsible){
44765 // this.split.el.on("dblclick", this.collapse, this);
44768 if(typeof this.config.minSize != "undefined"){
44769 this.split.minSize = this.config.minSize;
44771 if(typeof this.config.maxSize != "undefined"){
44772 this.split.maxSize = this.config.maxSize;
44774 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
44775 this.hideSplitter();
44780 getHMaxSize : function(){
44781 var cmax = this.config.maxSize || 10000;
44782 var center = this.mgr.getRegion("center");
44783 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
44786 getVMaxSize : function(){
44787 var cmax = this.config.maxSize || 10000;
44788 var center = this.mgr.getRegion("center");
44789 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
44792 onSplitMove : function(split, newSize){
44793 this.fireEvent("resized", this, newSize);
44797 * Returns the {@link Roo.SplitBar} for this region.
44798 * @return {Roo.SplitBar}
44800 getSplitBar : function(){
44805 this.hideSplitter();
44806 Roo.bootstrap.layout.Split.superclass.hide.call(this);
44809 hideSplitter : function(){
44811 this.split.el.setLocation(-2000,-2000);
44812 this.split.el.hide();
44818 this.split.el.show();
44820 Roo.bootstrap.layout.Split.superclass.show.call(this);
44823 beforeSlide: function(){
44824 if(Roo.isGecko){// firefox overflow auto bug workaround
44825 this.bodyEl.clip();
44827 this.tabs.bodyEl.clip();
44829 if(this.activePanel){
44830 this.activePanel.getEl().clip();
44832 if(this.activePanel.beforeSlide){
44833 this.activePanel.beforeSlide();
44839 afterSlide : function(){
44840 if(Roo.isGecko){// firefox overflow auto bug workaround
44841 this.bodyEl.unclip();
44843 this.tabs.bodyEl.unclip();
44845 if(this.activePanel){
44846 this.activePanel.getEl().unclip();
44847 if(this.activePanel.afterSlide){
44848 this.activePanel.afterSlide();
44854 initAutoHide : function(){
44855 if(this.autoHide !== false){
44856 if(!this.autoHideHd){
44857 var st = new Roo.util.DelayedTask(this.slideIn, this);
44858 this.autoHideHd = {
44859 "mouseout": function(e){
44860 if(!e.within(this.el, true)){
44864 "mouseover" : function(e){
44870 this.el.on(this.autoHideHd);
44874 clearAutoHide : function(){
44875 if(this.autoHide !== false){
44876 this.el.un("mouseout", this.autoHideHd.mouseout);
44877 this.el.un("mouseover", this.autoHideHd.mouseover);
44881 clearMonitor : function(){
44882 Roo.get(document).un("click", this.slideInIf, this);
44885 // these names are backwards but not changed for compat
44886 slideOut : function(){
44887 if(this.isSlid || this.el.hasActiveFx()){
44890 this.isSlid = true;
44891 if(this.collapseBtn){
44892 this.collapseBtn.hide();
44894 this.closeBtnState = this.closeBtn.getStyle('display');
44895 this.closeBtn.hide();
44897 this.stickBtn.show();
44900 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44901 this.beforeSlide();
44902 this.el.setStyle("z-index", 10001);
44903 this.el.slideIn(this.getSlideAnchor(), {
44904 callback: function(){
44906 this.initAutoHide();
44907 Roo.get(document).on("click", this.slideInIf, this);
44908 this.fireEvent("slideshow", this);
44915 afterSlideIn : function(){
44916 this.clearAutoHide();
44917 this.isSlid = false;
44918 this.clearMonitor();
44919 this.el.setStyle("z-index", "");
44920 if(this.collapseBtn){
44921 this.collapseBtn.show();
44923 this.closeBtn.setStyle('display', this.closeBtnState);
44925 this.stickBtn.hide();
44927 this.fireEvent("slidehide", this);
44930 slideIn : function(cb){
44931 if(!this.isSlid || this.el.hasActiveFx()){
44935 this.isSlid = false;
44936 this.beforeSlide();
44937 this.el.slideOut(this.getSlideAnchor(), {
44938 callback: function(){
44939 this.el.setLeftTop(-10000, -10000);
44941 this.afterSlideIn();
44949 slideInIf : function(e){
44950 if(!e.within(this.el)){
44955 animateCollapse : function(){
44956 this.beforeSlide();
44957 this.el.setStyle("z-index", 20000);
44958 var anchor = this.getSlideAnchor();
44959 this.el.slideOut(anchor, {
44960 callback : function(){
44961 this.el.setStyle("z-index", "");
44962 this.collapsedEl.slideIn(anchor, {duration:.3});
44964 this.el.setLocation(-10000,-10000);
44966 this.fireEvent("collapsed", this);
44973 animateExpand : function(){
44974 this.beforeSlide();
44975 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44976 this.el.setStyle("z-index", 20000);
44977 this.collapsedEl.hide({
44980 this.el.slideIn(this.getSlideAnchor(), {
44981 callback : function(){
44982 this.el.setStyle("z-index", "");
44985 this.split.el.show();
44987 this.fireEvent("invalidated", this);
44988 this.fireEvent("expanded", this);
45016 getAnchor : function(){
45017 return this.anchors[this.position];
45020 getCollapseAnchor : function(){
45021 return this.canchors[this.position];
45024 getSlideAnchor : function(){
45025 return this.sanchors[this.position];
45028 getAlignAdj : function(){
45029 var cm = this.cmargins;
45030 switch(this.position){
45046 getExpandAdj : function(){
45047 var c = this.collapsedEl, cm = this.cmargins;
45048 switch(this.position){
45050 return [-(cm.right+c.getWidth()+cm.left), 0];
45053 return [cm.right+c.getWidth()+cm.left, 0];
45056 return [0, -(cm.top+cm.bottom+c.getHeight())];
45059 return [0, cm.top+cm.bottom+c.getHeight()];
45065 * Ext JS Library 1.1.1
45066 * Copyright(c) 2006-2007, Ext JS, LLC.
45068 * Originally Released Under LGPL - original licence link has changed is not relivant.
45071 * <script type="text/javascript">
45074 * These classes are private internal classes
45076 Roo.bootstrap.layout.Center = function(config){
45077 config.region = "center";
45078 Roo.bootstrap.layout.Region.call(this, config);
45079 this.visible = true;
45080 this.minWidth = config.minWidth || 20;
45081 this.minHeight = config.minHeight || 20;
45084 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
45086 // center panel can't be hidden
45090 // center panel can't be hidden
45093 getMinWidth: function(){
45094 return this.minWidth;
45097 getMinHeight: function(){
45098 return this.minHeight;
45112 Roo.bootstrap.layout.North = function(config)
45114 config.region = 'north';
45115 config.cursor = 'n-resize';
45117 Roo.bootstrap.layout.Split.call(this, config);
45121 this.split.placement = Roo.bootstrap.SplitBar.TOP;
45122 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
45123 this.split.el.addClass("roo-layout-split-v");
45125 //var size = config.initialSize || config.height;
45126 //if(this.el && typeof size != "undefined"){
45127 // this.el.setHeight(size);
45130 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
45132 orientation: Roo.bootstrap.SplitBar.VERTICAL,
45135 onRender : function(ctr, pos)
45137 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45138 var size = this.config.initialSize || this.config.height;
45139 if(this.el && typeof size != "undefined"){
45140 this.el.setHeight(size);
45145 getBox : function(){
45146 if(this.collapsed){
45147 return this.collapsedEl.getBox();
45149 var box = this.el.getBox();
45151 box.height += this.split.el.getHeight();
45156 updateBox : function(box){
45157 if(this.split && !this.collapsed){
45158 box.height -= this.split.el.getHeight();
45159 this.split.el.setLeft(box.x);
45160 this.split.el.setTop(box.y+box.height);
45161 this.split.el.setWidth(box.width);
45163 if(this.collapsed){
45164 this.updateBody(box.width, null);
45166 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45174 Roo.bootstrap.layout.South = function(config){
45175 config.region = 'south';
45176 config.cursor = 's-resize';
45177 Roo.bootstrap.layout.Split.call(this, config);
45179 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
45180 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
45181 this.split.el.addClass("roo-layout-split-v");
45186 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
45187 orientation: Roo.bootstrap.SplitBar.VERTICAL,
45189 onRender : function(ctr, pos)
45191 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45192 var size = this.config.initialSize || this.config.height;
45193 if(this.el && typeof size != "undefined"){
45194 this.el.setHeight(size);
45199 getBox : function(){
45200 if(this.collapsed){
45201 return this.collapsedEl.getBox();
45203 var box = this.el.getBox();
45205 var sh = this.split.el.getHeight();
45212 updateBox : function(box){
45213 if(this.split && !this.collapsed){
45214 var sh = this.split.el.getHeight();
45217 this.split.el.setLeft(box.x);
45218 this.split.el.setTop(box.y-sh);
45219 this.split.el.setWidth(box.width);
45221 if(this.collapsed){
45222 this.updateBody(box.width, null);
45224 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45228 Roo.bootstrap.layout.East = function(config){
45229 config.region = "east";
45230 config.cursor = "e-resize";
45231 Roo.bootstrap.layout.Split.call(this, config);
45233 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
45234 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
45235 this.split.el.addClass("roo-layout-split-h");
45239 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
45240 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
45242 onRender : function(ctr, pos)
45244 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45245 var size = this.config.initialSize || this.config.width;
45246 if(this.el && typeof size != "undefined"){
45247 this.el.setWidth(size);
45252 getBox : function(){
45253 if(this.collapsed){
45254 return this.collapsedEl.getBox();
45256 var box = this.el.getBox();
45258 var sw = this.split.el.getWidth();
45265 updateBox : function(box){
45266 if(this.split && !this.collapsed){
45267 var sw = this.split.el.getWidth();
45269 this.split.el.setLeft(box.x);
45270 this.split.el.setTop(box.y);
45271 this.split.el.setHeight(box.height);
45274 if(this.collapsed){
45275 this.updateBody(null, box.height);
45277 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45281 Roo.bootstrap.layout.West = function(config){
45282 config.region = "west";
45283 config.cursor = "w-resize";
45285 Roo.bootstrap.layout.Split.call(this, config);
45287 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
45288 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
45289 this.split.el.addClass("roo-layout-split-h");
45293 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
45294 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
45296 onRender: function(ctr, pos)
45298 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
45299 var size = this.config.initialSize || this.config.width;
45300 if(typeof size != "undefined"){
45301 this.el.setWidth(size);
45305 getBox : function(){
45306 if(this.collapsed){
45307 return this.collapsedEl.getBox();
45309 var box = this.el.getBox();
45310 if (box.width == 0) {
45311 box.width = this.config.width; // kludge?
45314 box.width += this.split.el.getWidth();
45319 updateBox : function(box){
45320 if(this.split && !this.collapsed){
45321 var sw = this.split.el.getWidth();
45323 this.split.el.setLeft(box.x+box.width);
45324 this.split.el.setTop(box.y);
45325 this.split.el.setHeight(box.height);
45327 if(this.collapsed){
45328 this.updateBody(null, box.height);
45330 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45334 * Ext JS Library 1.1.1
45335 * Copyright(c) 2006-2007, Ext JS, LLC.
45337 * Originally Released Under LGPL - original licence link has changed is not relivant.
45340 * <script type="text/javascript">
45343 * @class Roo.bootstrap.paenl.Content
45344 * @extends Roo.util.Observable
45345 * @children Roo.bootstrap.Component
45346 * @parent builder Roo.bootstrap.layout.Border
45347 * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
45348 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
45349 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
45350 * @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
45351 * @cfg {Boolean} closable True if the panel can be closed/removed
45352 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
45353 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
45354 * @cfg {Toolbar} toolbar A toolbar for this panel
45355 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
45356 * @cfg {String} title The title for this panel
45357 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
45358 * @cfg {String} url Calls {@link #setUrl} with this value
45359 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
45360 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
45361 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
45362 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
45363 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
45364 * @cfg {Boolean} badges render the badges
45365 * @cfg {String} cls extra classes to use
45366 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
45369 * Create a new ContentPanel.
45370 * @param {String/Object} config A string to set only the title or a config object
45373 Roo.bootstrap.panel.Content = function( config){
45375 this.tpl = config.tpl || false;
45377 var el = config.el;
45378 var content = config.content;
45380 if(config.autoCreate){ // xtype is available if this is called from factory
45383 this.el = Roo.get(el);
45384 if(!this.el && config && config.autoCreate){
45385 if(typeof config.autoCreate == "object"){
45386 if(!config.autoCreate.id){
45387 config.autoCreate.id = config.id||el;
45389 this.el = Roo.DomHelper.append(document.body,
45390 config.autoCreate, true);
45394 cls: (config.cls || '') +
45395 (config.background ? ' bg-' + config.background : '') +
45396 " roo-layout-inactive-content",
45399 if (config.iframe) {
45403 style : 'border: 0px',
45404 src : 'data:text/html,%3Cbody%3E%3C%2Fbody%3E'
45410 elcfg.html = config.html;
45414 this.el = Roo.DomHelper.append(document.body, elcfg , true);
45415 if (config.iframe) {
45416 this.iframeEl = this.el.select('iframe',true).first();
45421 this.closable = false;
45422 this.loaded = false;
45423 this.active = false;
45426 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
45428 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
45430 this.wrapEl = this.el; //this.el.wrap();
45432 if (config.toolbar.items) {
45433 ti = config.toolbar.items ;
45434 delete config.toolbar.items ;
45438 this.toolbar.render(this.wrapEl, 'before');
45439 for(var i =0;i < ti.length;i++) {
45440 // Roo.log(['add child', items[i]]);
45441 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45443 this.toolbar.items = nitems;
45444 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
45445 delete config.toolbar;
45449 // xtype created footer. - not sure if will work as we normally have to render first..
45450 if (this.footer && !this.footer.el && this.footer.xtype) {
45451 if (!this.wrapEl) {
45452 this.wrapEl = this.el.wrap();
45455 this.footer.container = this.wrapEl.createChild();
45457 this.footer = Roo.factory(this.footer, Roo);
45462 if(typeof config == "string"){
45463 this.title = config;
45465 Roo.apply(this, config);
45469 this.resizeEl = Roo.get(this.resizeEl, true);
45471 this.resizeEl = this.el;
45473 // handle view.xtype
45481 * Fires when this panel is activated.
45482 * @param {Roo.ContentPanel} this
45486 * @event deactivate
45487 * Fires when this panel is activated.
45488 * @param {Roo.ContentPanel} this
45490 "deactivate" : true,
45494 * Fires when this panel is resized if fitToFrame is true.
45495 * @param {Roo.ContentPanel} this
45496 * @param {Number} width The width after any component adjustments
45497 * @param {Number} height The height after any component adjustments
45503 * Fires when this tab is created
45504 * @param {Roo.ContentPanel} this
45510 * Fires when this content is scrolled
45511 * @param {Roo.ContentPanel} this
45512 * @param {Event} scrollEvent
45523 if(this.autoScroll && !this.iframe){
45524 this.resizeEl.setStyle("overflow", "auto");
45525 this.resizeEl.on('scroll', this.onScroll, this);
45527 // fix randome scrolling
45528 //this.el.on('scroll', function() {
45529 // Roo.log('fix random scolling');
45530 // this.scrollTo('top',0);
45533 content = content || this.content;
45535 this.setContent(content);
45537 if(config && config.url){
45538 this.setUrl(this.url, this.params, this.loadOnce);
45543 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
45545 if (this.view && typeof(this.view.xtype) != 'undefined') {
45546 this.view.el = this.el.appendChild(document.createElement("div"));
45547 this.view = Roo.factory(this.view);
45548 this.view.render && this.view.render(false, '');
45552 this.fireEvent('render', this);
45555 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
45565 /* Resize Element - use this to work out scroll etc. */
45568 setRegion : function(region){
45569 this.region = region;
45570 this.setActiveClass(region && !this.background);
45574 setActiveClass: function(state)
45577 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
45578 this.el.setStyle('position','relative');
45580 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
45581 this.el.setStyle('position', 'absolute');
45586 * Returns the toolbar for this Panel if one was configured.
45587 * @return {Roo.Toolbar}
45589 getToolbar : function(){
45590 return this.toolbar;
45593 setActiveState : function(active)
45595 this.active = active;
45596 this.setActiveClass(active);
45598 if(this.fireEvent("deactivate", this) === false){
45603 this.fireEvent("activate", this);
45607 * Updates this panel's element (not for iframe)
45608 * @param {String} content The new content
45609 * @param {Boolean} loadScripts (optional) true to look for and process scripts
45611 setContent : function(content, loadScripts){
45616 this.el.update(content, loadScripts);
45619 ignoreResize : function(w, h)
45621 //return false; // always resize?
45622 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45625 this.lastSize = {width: w, height: h};
45630 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45631 * @return {Roo.UpdateManager} The UpdateManager
45633 getUpdateManager : function(){
45637 return this.el.getUpdateManager();
45640 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45641 * Does not work with IFRAME contents
45642 * @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:
45645 url: "your-url.php",
45646 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45647 callback: yourFunction,
45648 scope: yourObject, //(optional scope)
45651 text: "Loading...",
45657 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45658 * 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.
45659 * @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}
45660 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45661 * @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.
45662 * @return {Roo.ContentPanel} this
45670 var um = this.el.getUpdateManager();
45671 um.update.apply(um, arguments);
45677 * 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.
45678 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45679 * @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)
45680 * @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)
45681 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
45683 setUrl : function(url, params, loadOnce){
45685 this.iframeEl.dom.src = url;
45689 if(this.refreshDelegate){
45690 this.removeListener("activate", this.refreshDelegate);
45692 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45693 this.on("activate", this.refreshDelegate);
45694 return this.el.getUpdateManager();
45697 _handleRefresh : function(url, params, loadOnce){
45698 if(!loadOnce || !this.loaded){
45699 var updater = this.el.getUpdateManager();
45700 updater.update(url, params, this._setLoaded.createDelegate(this));
45704 _setLoaded : function(){
45705 this.loaded = true;
45709 * Returns this panel's id
45712 getId : function(){
45717 * Returns this panel's element - used by regiosn to add.
45718 * @return {Roo.Element}
45720 getEl : function(){
45721 return this.wrapEl || this.el;
45726 adjustForComponents : function(width, height)
45728 //Roo.log('adjustForComponents ');
45729 if(this.resizeEl != this.el){
45730 width -= this.el.getFrameWidth('lr');
45731 height -= this.el.getFrameWidth('tb');
45734 var te = this.toolbar.getEl();
45735 te.setWidth(width);
45736 height -= te.getHeight();
45739 var te = this.footer.getEl();
45740 te.setWidth(width);
45741 height -= te.getHeight();
45745 if(this.adjustments){
45746 width += this.adjustments[0];
45747 height += this.adjustments[1];
45749 return {"width": width, "height": height};
45752 setSize : function(width, height){
45753 if(this.fitToFrame && !this.ignoreResize(width, height)){
45754 if(this.fitContainer && this.resizeEl != this.el){
45755 this.el.setSize(width, height);
45757 var size = this.adjustForComponents(width, height);
45759 this.iframeEl.setSize(width,height);
45762 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45763 this.fireEvent('resize', this, size.width, size.height);
45770 * Returns this panel's title
45773 getTitle : function(){
45775 if (typeof(this.title) != 'object') {
45780 for (var k in this.title) {
45781 if (!this.title.hasOwnProperty(k)) {
45785 if (k.indexOf('-') >= 0) {
45786 var s = k.split('-');
45787 for (var i = 0; i<s.length; i++) {
45788 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
45791 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
45798 * Set this panel's title
45799 * @param {String} title
45801 setTitle : function(title){
45802 this.title = title;
45804 this.region.updatePanelTitle(this, title);
45809 * Returns true is this panel was configured to be closable
45810 * @return {Boolean}
45812 isClosable : function(){
45813 return this.closable;
45816 beforeSlide : function(){
45818 this.resizeEl.clip();
45821 afterSlide : function(){
45823 this.resizeEl.unclip();
45827 * Force a content refresh from the URL specified in the {@link #setUrl} method.
45828 * Will fail silently if the {@link #setUrl} method has not been called.
45829 * This does not activate the panel, just updates its content.
45831 refresh : function(){
45832 if(this.refreshDelegate){
45833 this.loaded = false;
45834 this.refreshDelegate();
45839 * Destroys this panel
45841 destroy : function(){
45842 this.el.removeAllListeners();
45843 var tempEl = document.createElement("span");
45844 tempEl.appendChild(this.el.dom);
45845 tempEl.innerHTML = "";
45851 * form - if the content panel contains a form - this is a reference to it.
45852 * @type {Roo.form.Form}
45856 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
45857 * This contains a reference to it.
45863 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45873 * @param {Object} cfg Xtype definition of item to add.
45877 getChildContainer: function () {
45878 return this.getEl();
45882 onScroll : function(e)
45884 this.fireEvent('scroll', this, e);
45889 var ret = new Roo.factory(cfg);
45894 if (cfg.xtype.match(/^Form$/)) {
45897 //if (this.footer) {
45898 // el = this.footer.container.insertSibling(false, 'before');
45900 el = this.el.createChild();
45903 this.form = new Roo.form.Form(cfg);
45906 if ( this.form.allItems.length) {
45907 this.form.render(el.dom);
45911 // should only have one of theses..
45912 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
45913 // views.. should not be just added - used named prop 'view''
45915 cfg.el = this.el.appendChild(document.createElement("div"));
45918 var ret = new Roo.factory(cfg);
45920 ret.render && ret.render(false, ''); // render blank..
45930 * @class Roo.bootstrap.panel.Grid
45931 * @extends Roo.bootstrap.panel.Content
45933 * Create a new GridPanel.
45934 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
45935 * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
45936 * @param {Object} config A the config object
45942 Roo.bootstrap.panel.Grid = function(config)
45946 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45947 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45949 config.el = this.wrapper;
45950 //this.el = this.wrapper;
45952 if (config.container) {
45953 // ctor'ed from a Border/panel.grid
45956 this.wrapper.setStyle("overflow", "hidden");
45957 this.wrapper.addClass('roo-grid-container');
45962 if(config.toolbar){
45963 var tool_el = this.wrapper.createChild();
45964 this.toolbar = Roo.factory(config.toolbar);
45966 if (config.toolbar.items) {
45967 ti = config.toolbar.items ;
45968 delete config.toolbar.items ;
45972 this.toolbar.render(tool_el);
45973 for(var i =0;i < ti.length;i++) {
45974 // Roo.log(['add child', items[i]]);
45975 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45977 this.toolbar.items = nitems;
45979 delete config.toolbar;
45982 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45983 config.grid.scrollBody = true;;
45984 config.grid.monitorWindowResize = false; // turn off autosizing
45985 config.grid.autoHeight = false;
45986 config.grid.autoWidth = false;
45988 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45990 if (config.background) {
45991 // render grid on panel activation (if panel background)
45992 this.on('activate', function(gp) {
45993 if (!gp.grid.rendered) {
45994 gp.grid.render(this.wrapper);
45995 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
46000 this.grid.render(this.wrapper);
46001 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
46004 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
46005 // ??? needed ??? config.el = this.wrapper;
46010 // xtype created footer. - not sure if will work as we normally have to render first..
46011 if (this.footer && !this.footer.el && this.footer.xtype) {
46013 var ctr = this.grid.getView().getFooterPanel(true);
46014 this.footer.dataSource = this.grid.dataSource;
46015 this.footer = Roo.factory(this.footer, Roo);
46016 this.footer.render(ctr);
46026 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
46029 getId : function(){
46030 return this.grid.id;
46034 * Returns the grid for this panel
46035 * @return {Roo.bootstrap.Table}
46037 getGrid : function(){
46041 setSize : function(width, height)
46044 //if(!this.ignoreResize(width, height)){
46045 var grid = this.grid;
46046 var size = this.adjustForComponents(width, height);
46047 // tfoot is not a footer?
46050 var gridel = grid.getGridEl();
46051 gridel.setSize(size.width, size.height);
46053 var tbd = grid.getGridEl().select('tbody', true).first();
46054 var thd = grid.getGridEl().select('thead',true).first();
46055 var tbf= grid.getGridEl().select('tfoot', true).first();
46058 size.height -= tbf.getHeight();
46061 size.height -= thd.getHeight();
46064 tbd.setSize(size.width, size.height );
46065 // this is for the account management tab -seems to work there.
46066 var thd = grid.getGridEl().select('thead',true).first();
46068 // tbd.setSize(size.width, size.height - thd.getHeight());
46078 beforeSlide : function(){
46079 this.grid.getView().scroller.clip();
46082 afterSlide : function(){
46083 this.grid.getView().scroller.unclip();
46086 destroy : function(){
46087 this.grid.destroy();
46089 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
46094 * @class Roo.bootstrap.panel.Nest
46095 * @extends Roo.bootstrap.panel.Content
46097 * Create a new Panel, that can contain a layout.Border.
46100 * @param {String/Object} config A string to set only the title or a config object
46102 Roo.bootstrap.panel.Nest = function(config)
46104 // construct with only one argument..
46105 /* FIXME - implement nicer consturctors
46106 if (layout.layout) {
46108 layout = config.layout;
46109 delete config.layout;
46111 if (layout.xtype && !layout.getEl) {
46112 // then layout needs constructing..
46113 layout = Roo.factory(layout, Roo);
46117 config.el = config.layout.getEl();
46119 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
46121 config.layout.monitorWindowResize = false; // turn off autosizing
46122 this.layout = config.layout;
46123 this.layout.getEl().addClass("roo-layout-nested-layout");
46124 this.layout.parent = this;
46131 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
46133 * @cfg {Roo.BorderLayout} layout The layout for this panel
46137 setSize : function(width, height){
46138 if(!this.ignoreResize(width, height)){
46139 var size = this.adjustForComponents(width, height);
46140 var el = this.layout.getEl();
46141 if (size.height < 1) {
46142 el.setWidth(size.width);
46144 el.setSize(size.width, size.height);
46146 var touch = el.dom.offsetWidth;
46147 this.layout.layout();
46148 // ie requires a double layout on the first pass
46149 if(Roo.isIE && !this.initialized){
46150 this.initialized = true;
46151 this.layout.layout();
46156 // activate all subpanels if not currently active..
46158 setActiveState : function(active){
46159 this.active = active;
46160 this.setActiveClass(active);
46163 this.fireEvent("deactivate", this);
46167 this.fireEvent("activate", this);
46168 // not sure if this should happen before or after..
46169 if (!this.layout) {
46170 return; // should not happen..
46173 for (var r in this.layout.regions) {
46174 reg = this.layout.getRegion(r);
46175 if (reg.getActivePanel()) {
46176 //reg.showPanel(reg.getActivePanel()); // force it to activate..
46177 reg.setActivePanel(reg.getActivePanel());
46180 if (!reg.panels.length) {
46183 reg.showPanel(reg.getPanel(0));
46192 * Returns the nested BorderLayout for this panel
46193 * @return {Roo.BorderLayout}
46195 getLayout : function(){
46196 return this.layout;
46200 * Adds a xtype elements to the layout of the nested panel
46204 xtype : 'ContentPanel',
46211 xtype : 'NestedLayoutPanel',
46217 items : [ ... list of content panels or nested layout panels.. ]
46221 * @param {Object} cfg Xtype definition of item to add.
46223 addxtype : function(cfg) {
46224 return this.layout.addxtype(cfg);
46229 * Ext JS Library 1.1.1
46230 * Copyright(c) 2006-2007, Ext JS, LLC.
46232 * Originally Released Under LGPL - original licence link has changed is not relivant.
46235 * <script type="text/javascript">
46238 * @class Roo.TabPanel
46239 * @extends Roo.util.Observable
46240 * A lightweight tab container.
46244 // basic tabs 1, built from existing content
46245 var tabs = new Roo.TabPanel("tabs1");
46246 tabs.addTab("script", "View Script");
46247 tabs.addTab("markup", "View Markup");
46248 tabs.activate("script");
46250 // more advanced tabs, built from javascript
46251 var jtabs = new Roo.TabPanel("jtabs");
46252 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
46254 // set up the UpdateManager
46255 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
46256 var updater = tab2.getUpdateManager();
46257 updater.setDefaultUrl("ajax1.htm");
46258 tab2.on('activate', updater.refresh, updater, true);
46260 // Use setUrl for Ajax loading
46261 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
46262 tab3.setUrl("ajax2.htm", null, true);
46265 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
46268 jtabs.activate("jtabs-1");
46271 * Create a new TabPanel.
46272 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
46273 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
46275 Roo.bootstrap.panel.Tabs = function(config){
46277 * The container element for this TabPanel.
46278 * @type Roo.Element
46280 this.el = Roo.get(config.el);
46283 if(typeof config == "boolean"){
46284 this.tabPosition = config ? "bottom" : "top";
46286 Roo.apply(this, config);
46290 if(this.tabPosition == "bottom"){
46291 // if tabs are at the bottom = create the body first.
46292 this.bodyEl = Roo.get(this.createBody(this.el.dom));
46293 this.el.addClass("roo-tabs-bottom");
46295 // next create the tabs holders
46297 if (this.tabPosition == "west"){
46299 var reg = this.region; // fake it..
46301 if (!reg.mgr.parent) {
46304 reg = reg.mgr.parent.region;
46306 Roo.log("got nest?");
46308 if (reg.mgr.getRegion('west')) {
46309 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
46310 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
46311 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
46312 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
46313 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
46321 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
46322 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
46323 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
46324 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
46329 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
46332 // finally - if tabs are at the top, then create the body last..
46333 if(this.tabPosition != "bottom"){
46334 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
46335 * @type Roo.Element
46337 this.bodyEl = Roo.get(this.createBody(this.el.dom));
46338 this.el.addClass("roo-tabs-top");
46342 this.bodyEl.setStyle("position", "relative");
46344 this.active = null;
46345 this.activateDelegate = this.activate.createDelegate(this);
46350 * Fires when the active tab changes
46351 * @param {Roo.TabPanel} this
46352 * @param {Roo.TabPanelItem} activePanel The new active tab
46356 * @event beforetabchange
46357 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
46358 * @param {Roo.TabPanel} this
46359 * @param {Object} e Set cancel to true on this object to cancel the tab change
46360 * @param {Roo.TabPanelItem} tab The tab being changed to
46362 "beforetabchange" : true
46365 Roo.EventManager.onWindowResize(this.onResize, this);
46366 this.cpad = this.el.getPadding("lr");
46367 this.hiddenCount = 0;
46370 // toolbar on the tabbar support...
46371 if (this.toolbar) {
46372 alert("no toolbar support yet");
46373 this.toolbar = false;
46375 var tcfg = this.toolbar;
46376 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
46377 this.toolbar = new Roo.Toolbar(tcfg);
46378 if (Roo.isSafari) {
46379 var tbl = tcfg.container.child('table', true);
46380 tbl.setAttribute('width', '100%');
46388 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
46391 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
46393 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
46395 tabPosition : "top",
46397 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
46399 currentTabWidth : 0,
46401 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
46405 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
46409 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
46411 preferredTabWidth : 175,
46413 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
46415 resizeTabs : false,
46417 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
46419 monitorResize : true,
46421 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
46423 toolbar : false, // set by caller..
46425 region : false, /// set by caller
46427 disableTooltips : true, // not used yet...
46430 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
46431 * @param {String} id The id of the div to use <b>or create</b>
46432 * @param {String} text The text for the tab
46433 * @param {String} content (optional) Content to put in the TabPanelItem body
46434 * @param {Boolean} closable (optional) True to create a close icon on the tab
46435 * @return {Roo.TabPanelItem} The created TabPanelItem
46437 addTab : function(id, text, content, closable, tpl)
46439 var item = new Roo.bootstrap.panel.TabItem({
46443 closable : closable,
46446 this.addTabItem(item);
46448 item.setContent(content);
46454 * Returns the {@link Roo.TabPanelItem} with the specified id/index
46455 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
46456 * @return {Roo.TabPanelItem}
46458 getTab : function(id){
46459 return this.items[id];
46463 * Hides the {@link Roo.TabPanelItem} with the specified id/index
46464 * @param {String/Number} id The id or index of the TabPanelItem to hide.
46466 hideTab : function(id){
46467 var t = this.items[id];
46470 this.hiddenCount++;
46471 this.autoSizeTabs();
46476 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
46477 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
46479 unhideTab : function(id){
46480 var t = this.items[id];
46482 t.setHidden(false);
46483 this.hiddenCount--;
46484 this.autoSizeTabs();
46489 * Adds an existing {@link Roo.TabPanelItem}.
46490 * @param {Roo.TabPanelItem} item The TabPanelItem to add
46492 addTabItem : function(item)
46494 this.items[item.id] = item;
46495 this.items.push(item);
46496 this.autoSizeTabs();
46497 // if(this.resizeTabs){
46498 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
46499 // this.autoSizeTabs();
46501 // item.autoSize();
46506 * Removes a {@link Roo.TabPanelItem}.
46507 * @param {String/Number} id The id or index of the TabPanelItem to remove.
46509 removeTab : function(id){
46510 var items = this.items;
46511 var tab = items[id];
46512 if(!tab) { return; }
46513 var index = items.indexOf(tab);
46514 if(this.active == tab && items.length > 1){
46515 var newTab = this.getNextAvailable(index);
46520 this.stripEl.dom.removeChild(tab.pnode.dom);
46521 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
46522 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
46524 items.splice(index, 1);
46525 delete this.items[tab.id];
46526 tab.fireEvent("close", tab);
46527 tab.purgeListeners();
46528 this.autoSizeTabs();
46531 getNextAvailable : function(start){
46532 var items = this.items;
46534 // look for a next tab that will slide over to
46535 // replace the one being removed
46536 while(index < items.length){
46537 var item = items[++index];
46538 if(item && !item.isHidden()){
46542 // if one isn't found select the previous tab (on the left)
46545 var item = items[--index];
46546 if(item && !item.isHidden()){
46554 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
46555 * @param {String/Number} id The id or index of the TabPanelItem to disable.
46557 disableTab : function(id){
46558 var tab = this.items[id];
46559 if(tab && this.active != tab){
46565 * Enables a {@link Roo.TabPanelItem} that is disabled.
46566 * @param {String/Number} id The id or index of the TabPanelItem to enable.
46568 enableTab : function(id){
46569 var tab = this.items[id];
46574 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
46575 * @param {String/Number} id The id or index of the TabPanelItem to activate.
46576 * @return {Roo.TabPanelItem} The TabPanelItem.
46578 activate : function(id)
46580 //Roo.log('activite:' + id);
46582 var tab = this.items[id];
46586 if(tab == this.active || tab.disabled){
46590 this.fireEvent("beforetabchange", this, e, tab);
46591 if(e.cancel !== true && !tab.disabled){
46593 this.active.hide();
46595 this.active = this.items[id];
46596 this.active.show();
46597 this.fireEvent("tabchange", this, this.active);
46603 * Gets the active {@link Roo.TabPanelItem}.
46604 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
46606 getActiveTab : function(){
46607 return this.active;
46611 * Updates the tab body element to fit the height of the container element
46612 * for overflow scrolling
46613 * @param {Number} targetHeight (optional) Override the starting height from the elements height
46615 syncHeight : function(targetHeight){
46616 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
46617 var bm = this.bodyEl.getMargins();
46618 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
46619 this.bodyEl.setHeight(newHeight);
46623 onResize : function(){
46624 if(this.monitorResize){
46625 this.autoSizeTabs();
46630 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
46632 beginUpdate : function(){
46633 this.updating = true;
46637 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
46639 endUpdate : function(){
46640 this.updating = false;
46641 this.autoSizeTabs();
46645 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
46647 autoSizeTabs : function()
46649 var count = this.items.length;
46650 var vcount = count - this.hiddenCount;
46653 this.stripEl.hide();
46655 this.stripEl.show();
46658 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
46663 var w = Math.max(this.el.getWidth() - this.cpad, 10);
46664 var availWidth = Math.floor(w / vcount);
46665 var b = this.stripBody;
46666 if(b.getWidth() > w){
46667 var tabs = this.items;
46668 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
46669 if(availWidth < this.minTabWidth){
46670 /*if(!this.sleft){ // incomplete scrolling code
46671 this.createScrollButtons();
46674 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
46677 if(this.currentTabWidth < this.preferredTabWidth){
46678 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
46684 * Returns the number of tabs in this TabPanel.
46687 getCount : function(){
46688 return this.items.length;
46692 * Resizes all the tabs to the passed width
46693 * @param {Number} The new width
46695 setTabWidth : function(width){
46696 this.currentTabWidth = width;
46697 for(var i = 0, len = this.items.length; i < len; i++) {
46698 if(!this.items[i].isHidden()) {
46699 this.items[i].setWidth(width);
46705 * Destroys this TabPanel
46706 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
46708 destroy : function(removeEl){
46709 Roo.EventManager.removeResizeListener(this.onResize, this);
46710 for(var i = 0, len = this.items.length; i < len; i++){
46711 this.items[i].purgeListeners();
46713 if(removeEl === true){
46714 this.el.update("");
46719 createStrip : function(container)
46721 var strip = document.createElement("nav");
46722 strip.className = Roo.bootstrap.version == 4 ?
46723 "navbar-light bg-light" :
46724 "navbar navbar-default"; //"x-tabs-wrap";
46725 container.appendChild(strip);
46729 createStripList : function(strip)
46731 // div wrapper for retard IE
46732 // returns the "tr" element.
46733 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
46734 //'<div class="x-tabs-strip-wrap">'+
46735 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
46736 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
46737 return strip.firstChild; //.firstChild.firstChild.firstChild;
46739 createBody : function(container)
46741 var body = document.createElement("div");
46742 Roo.id(body, "tab-body");
46743 //Roo.fly(body).addClass("x-tabs-body");
46744 Roo.fly(body).addClass("tab-content");
46745 container.appendChild(body);
46748 createItemBody :function(bodyEl, id){
46749 var body = Roo.getDom(id);
46751 body = document.createElement("div");
46754 //Roo.fly(body).addClass("x-tabs-item-body");
46755 Roo.fly(body).addClass("tab-pane");
46756 bodyEl.insertBefore(body, bodyEl.firstChild);
46760 createStripElements : function(stripEl, text, closable, tpl)
46762 var td = document.createElement("li"); // was td..
46763 td.className = 'nav-item';
46765 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
46768 stripEl.appendChild(td);
46770 td.className = "x-tabs-closable";
46771 if(!this.closeTpl){
46772 this.closeTpl = new Roo.Template(
46773 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46774 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
46775 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
46778 var el = this.closeTpl.overwrite(td, {"text": text});
46779 var close = el.getElementsByTagName("div")[0];
46780 var inner = el.getElementsByTagName("em")[0];
46781 return {"el": el, "close": close, "inner": inner};
46784 // not sure what this is..
46785 // if(!this.tabTpl){
46786 //this.tabTpl = new Roo.Template(
46787 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46788 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
46790 // this.tabTpl = new Roo.Template(
46791 // '<a href="#">' +
46792 // '<span unselectable="on"' +
46793 // (this.disableTooltips ? '' : ' title="{text}"') +
46794 // ' >{text}</span></a>'
46800 var template = tpl || this.tabTpl || false;
46803 template = new Roo.Template(
46804 Roo.bootstrap.version == 4 ?
46806 '<a class="nav-link" href="#" unselectable="on"' +
46807 (this.disableTooltips ? '' : ' title="{text}"') +
46810 '<a class="nav-link" href="#">' +
46811 '<span unselectable="on"' +
46812 (this.disableTooltips ? '' : ' title="{text}"') +
46813 ' >{text}</span></a>'
46818 switch (typeof(template)) {
46822 template = new Roo.Template(template);
46828 var el = template.overwrite(td, {"text": text});
46830 var inner = el.getElementsByTagName("span")[0];
46832 return {"el": el, "inner": inner};
46840 * @class Roo.TabPanelItem
46841 * @extends Roo.util.Observable
46842 * Represents an individual item (tab plus body) in a TabPanel.
46843 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
46844 * @param {String} id The id of this TabPanelItem
46845 * @param {String} text The text for the tab of this TabPanelItem
46846 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
46848 Roo.bootstrap.panel.TabItem = function(config){
46850 * The {@link Roo.TabPanel} this TabPanelItem belongs to
46851 * @type Roo.TabPanel
46853 this.tabPanel = config.panel;
46855 * The id for this TabPanelItem
46858 this.id = config.id;
46860 this.disabled = false;
46862 this.text = config.text;
46864 this.loaded = false;
46865 this.closable = config.closable;
46868 * The body element for this TabPanelItem.
46869 * @type Roo.Element
46871 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
46872 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
46873 this.bodyEl.setStyle("display", "block");
46874 this.bodyEl.setStyle("zoom", "1");
46875 //this.hideAction();
46877 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
46879 this.el = Roo.get(els.el);
46880 this.inner = Roo.get(els.inner, true);
46881 this.textEl = Roo.bootstrap.version == 4 ?
46882 this.el : Roo.get(this.el.dom.firstChild, true);
46884 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
46885 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
46888 // this.el.on("mousedown", this.onTabMouseDown, this);
46889 this.el.on("click", this.onTabClick, this);
46891 if(config.closable){
46892 var c = Roo.get(els.close, true);
46893 c.dom.title = this.closeText;
46894 c.addClassOnOver("close-over");
46895 c.on("click", this.closeClick, this);
46901 * Fires when this tab becomes the active tab.
46902 * @param {Roo.TabPanel} tabPanel The parent TabPanel
46903 * @param {Roo.TabPanelItem} this
46907 * @event beforeclose
46908 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
46909 * @param {Roo.TabPanelItem} this
46910 * @param {Object} e Set cancel to true on this object to cancel the close.
46912 "beforeclose": true,
46915 * Fires when this tab is closed.
46916 * @param {Roo.TabPanelItem} this
46920 * @event deactivate
46921 * Fires when this tab is no longer the active tab.
46922 * @param {Roo.TabPanel} tabPanel The parent TabPanel
46923 * @param {Roo.TabPanelItem} this
46925 "deactivate" : true
46927 this.hidden = false;
46929 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
46932 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
46934 purgeListeners : function(){
46935 Roo.util.Observable.prototype.purgeListeners.call(this);
46936 this.el.removeAllListeners();
46939 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
46942 this.status_node.addClass("active");
46945 this.tabPanel.stripWrap.repaint();
46947 this.fireEvent("activate", this.tabPanel, this);
46951 * Returns true if this tab is the active tab.
46952 * @return {Boolean}
46954 isActive : function(){
46955 return this.tabPanel.getActiveTab() == this;
46959 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46962 this.status_node.removeClass("active");
46964 this.fireEvent("deactivate", this.tabPanel, this);
46967 hideAction : function(){
46968 this.bodyEl.hide();
46969 this.bodyEl.setStyle("position", "absolute");
46970 this.bodyEl.setLeft("-20000px");
46971 this.bodyEl.setTop("-20000px");
46974 showAction : function(){
46975 this.bodyEl.setStyle("position", "relative");
46976 this.bodyEl.setTop("");
46977 this.bodyEl.setLeft("");
46978 this.bodyEl.show();
46982 * Set the tooltip for the tab.
46983 * @param {String} tooltip The tab's tooltip
46985 setTooltip : function(text){
46986 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46987 this.textEl.dom.qtip = text;
46988 this.textEl.dom.removeAttribute('title');
46990 this.textEl.dom.title = text;
46994 onTabClick : function(e){
46995 e.preventDefault();
46996 this.tabPanel.activate(this.id);
46999 onTabMouseDown : function(e){
47000 e.preventDefault();
47001 this.tabPanel.activate(this.id);
47004 getWidth : function(){
47005 return this.inner.getWidth();
47008 setWidth : function(width){
47009 var iwidth = width - this.linode.getPadding("lr");
47010 this.inner.setWidth(iwidth);
47011 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
47012 this.linode.setWidth(width);
47016 * Show or hide the tab
47017 * @param {Boolean} hidden True to hide or false to show.
47019 setHidden : function(hidden){
47020 this.hidden = hidden;
47021 this.linode.setStyle("display", hidden ? "none" : "");
47025 * Returns true if this tab is "hidden"
47026 * @return {Boolean}
47028 isHidden : function(){
47029 return this.hidden;
47033 * Returns the text for this tab
47036 getText : function(){
47040 autoSize : function(){
47041 //this.el.beginMeasure();
47042 this.textEl.setWidth(1);
47044 * #2804 [new] Tabs in Roojs
47045 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
47047 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
47048 //this.el.endMeasure();
47052 * Sets the text for the tab (Note: this also sets the tooltip text)
47053 * @param {String} text The tab's text and tooltip
47055 setText : function(text){
47057 this.textEl.update(text);
47058 this.setTooltip(text);
47059 //if(!this.tabPanel.resizeTabs){
47060 // this.autoSize();
47064 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
47066 activate : function(){
47067 this.tabPanel.activate(this.id);
47071 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
47073 disable : function(){
47074 if(this.tabPanel.active != this){
47075 this.disabled = true;
47076 this.status_node.addClass("disabled");
47081 * Enables this TabPanelItem if it was previously disabled.
47083 enable : function(){
47084 this.disabled = false;
47085 this.status_node.removeClass("disabled");
47089 * Sets the content for this TabPanelItem.
47090 * @param {String} content The content
47091 * @param {Boolean} loadScripts true to look for and load scripts
47093 setContent : function(content, loadScripts){
47094 this.bodyEl.update(content, loadScripts);
47098 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
47099 * @return {Roo.UpdateManager} The UpdateManager
47101 getUpdateManager : function(){
47102 return this.bodyEl.getUpdateManager();
47106 * Set a URL to be used to load the content for this TabPanelItem.
47107 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
47108 * @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)
47109 * @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)
47110 * @return {Roo.UpdateManager} The UpdateManager
47112 setUrl : function(url, params, loadOnce){
47113 if(this.refreshDelegate){
47114 this.un('activate', this.refreshDelegate);
47116 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
47117 this.on("activate", this.refreshDelegate);
47118 return this.bodyEl.getUpdateManager();
47122 _handleRefresh : function(url, params, loadOnce){
47123 if(!loadOnce || !this.loaded){
47124 var updater = this.bodyEl.getUpdateManager();
47125 updater.update(url, params, this._setLoaded.createDelegate(this));
47130 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
47131 * Will fail silently if the setUrl method has not been called.
47132 * This does not activate the panel, just updates its content.
47134 refresh : function(){
47135 if(this.refreshDelegate){
47136 this.loaded = false;
47137 this.refreshDelegate();
47142 _setLoaded : function(){
47143 this.loaded = true;
47147 closeClick : function(e){
47150 this.fireEvent("beforeclose", this, o);
47151 if(o.cancel !== true){
47152 this.tabPanel.removeTab(this.id);
47156 * The text displayed in the tooltip for the close icon.
47159 closeText : "Close this tab"
47162 * This script refer to:
47163 * Title: International Telephone Input
47164 * Author: Jack O'Connor
47165 * Code version: v12.1.12
47166 * Availability: https://github.com/jackocnr/intl-tel-input.git
47169 Roo.bootstrap.form.PhoneInputData = function() {
47172 "Afghanistan (افغانستان)",
47177 "Albania (Shqipëri)",
47182 "Algeria (الجزائر)",
47207 "Antigua and Barbuda",
47217 "Armenia (Հայաստան)",
47233 "Austria (Österreich)",
47238 "Azerbaijan (Azərbaycan)",
47248 "Bahrain (البحرين)",
47253 "Bangladesh (বাংলাদেশ)",
47263 "Belarus (Беларусь)",
47268 "Belgium (België)",
47298 "Bosnia and Herzegovina (Босна и Херцеговина)",
47313 "British Indian Ocean Territory",
47318 "British Virgin Islands",
47328 "Bulgaria (България)",
47338 "Burundi (Uburundi)",
47343 "Cambodia (កម្ពុជា)",
47348 "Cameroon (Cameroun)",
47357 ["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"]
47360 "Cape Verde (Kabu Verdi)",
47365 "Caribbean Netherlands",
47376 "Central African Republic (République centrafricaine)",
47396 "Christmas Island",
47402 "Cocos (Keeling) Islands",
47413 "Comoros (جزر القمر)",
47418 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
47423 "Congo (Republic) (Congo-Brazzaville)",
47443 "Croatia (Hrvatska)",
47464 "Czech Republic (Česká republika)",
47469 "Denmark (Danmark)",
47484 "Dominican Republic (República Dominicana)",
47488 ["809", "829", "849"]
47506 "Equatorial Guinea (Guinea Ecuatorial)",
47526 "Falkland Islands (Islas Malvinas)",
47531 "Faroe Islands (Føroyar)",
47552 "French Guiana (Guyane française)",
47557 "French Polynesia (Polynésie française)",
47572 "Georgia (საქართველო)",
47577 "Germany (Deutschland)",
47597 "Greenland (Kalaallit Nunaat)",
47634 "Guinea-Bissau (Guiné Bissau)",
47659 "Hungary (Magyarország)",
47664 "Iceland (Ísland)",
47684 "Iraq (العراق)",
47700 "Israel (ישראל)",
47727 "Jordan (الأردن)",
47732 "Kazakhstan (Казахстан)",
47753 "Kuwait (الكويت)",
47758 "Kyrgyzstan (Кыргызстан)",
47768 "Latvia (Latvija)",
47773 "Lebanon (لبنان)",
47788 "Libya (ليبيا)",
47798 "Lithuania (Lietuva)",
47813 "Macedonia (FYROM) (Македонија)",
47818 "Madagascar (Madagasikara)",
47848 "Marshall Islands",
47858 "Mauritania (موريتانيا)",
47863 "Mauritius (Moris)",
47884 "Moldova (Republica Moldova)",
47894 "Mongolia (Монгол)",
47899 "Montenegro (Crna Gora)",
47909 "Morocco (المغرب)",
47915 "Mozambique (Moçambique)",
47920 "Myanmar (Burma) (မြန်မာ)",
47925 "Namibia (Namibië)",
47940 "Netherlands (Nederland)",
47945 "New Caledonia (Nouvelle-Calédonie)",
47980 "North Korea (조선 민주주의 인민 공화국)",
47985 "Northern Mariana Islands",
48001 "Pakistan (پاکستان)",
48011 "Palestine (فلسطين)",
48021 "Papua New Guinea",
48063 "Réunion (La Réunion)",
48069 "Romania (România)",
48085 "Saint Barthélemy",
48096 "Saint Kitts and Nevis",
48106 "Saint Martin (Saint-Martin (partie française))",
48112 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
48117 "Saint Vincent and the Grenadines",
48132 "São Tomé and Príncipe (São Tomé e Príncipe)",
48137 "Saudi Arabia (المملكة العربية السعودية)",
48142 "Senegal (Sénégal)",
48172 "Slovakia (Slovensko)",
48177 "Slovenia (Slovenija)",
48187 "Somalia (Soomaaliya)",
48197 "South Korea (대한민국)",
48202 "South Sudan (جنوب السودان)",
48212 "Sri Lanka (ශ්රී ලංකාව)",
48217 "Sudan (السودان)",
48227 "Svalbard and Jan Mayen",
48238 "Sweden (Sverige)",
48243 "Switzerland (Schweiz)",
48248 "Syria (سوريا)",
48293 "Trinidad and Tobago",
48298 "Tunisia (تونس)",
48303 "Turkey (Türkiye)",
48313 "Turks and Caicos Islands",
48323 "U.S. Virgin Islands",
48333 "Ukraine (Україна)",
48338 "United Arab Emirates (الإمارات العربية المتحدة)",
48360 "Uzbekistan (Oʻzbekiston)",
48370 "Vatican City (Città del Vaticano)",
48381 "Vietnam (Việt Nam)",
48386 "Wallis and Futuna (Wallis-et-Futuna)",
48391 "Western Sahara (الصحراء الغربية)",
48397 "Yemen (اليمن)",
48421 * This script refer to:
48422 * Title: International Telephone Input
48423 * Author: Jack O'Connor
48424 * Code version: v12.1.12
48425 * Availability: https://github.com/jackocnr/intl-tel-input.git
48429 * @class Roo.bootstrap.form.PhoneInput
48430 * @extends Roo.bootstrap.form.TriggerField
48431 * An input with International dial-code selection
48433 * @cfg {String} defaultDialCode default '+852'
48434 * @cfg {Array} preferedCountries default []
48437 * Create a new PhoneInput.
48438 * @param {Object} config Configuration options
48441 Roo.bootstrap.form.PhoneInput = function(config) {
48442 Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
48445 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
48447 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
48449 listWidth: undefined,
48451 selectedClass: 'active',
48453 invalidClass : "has-warning",
48455 validClass: 'has-success',
48457 allowed: '0123456789',
48462 * @cfg {String} defaultDialCode The default dial code when initializing the input
48464 defaultDialCode: '+852',
48467 * @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
48469 preferedCountries: false,
48471 getAutoCreate : function()
48473 var data = Roo.bootstrap.form.PhoneInputData();
48474 var align = this.labelAlign || this.parentLabelAlign();
48477 this.allCountries = [];
48478 this.dialCodeMapping = [];
48480 for (var i = 0; i < data.length; i++) {
48482 this.allCountries[i] = {
48486 priority: c[3] || 0,
48487 areaCodes: c[4] || null
48489 this.dialCodeMapping[c[2]] = {
48492 priority: c[3] || 0,
48493 areaCodes: c[4] || null
48505 // type: 'number', -- do not use number - we get the flaky up/down arrows.
48506 maxlength: this.max_length,
48507 cls : 'form-control tel-input',
48508 autocomplete: 'new-password'
48511 var hiddenInput = {
48514 cls: 'hidden-tel-input'
48518 hiddenInput.name = this.name;
48521 if (this.disabled) {
48522 input.disabled = true;
48525 var flag_container = {
48542 cls: this.hasFeedback ? 'has-feedback' : '',
48548 cls: 'dial-code-holder',
48555 cls: 'roo-select2-container input-group',
48562 if (this.fieldLabel.length) {
48565 tooltip: 'This field is required'
48571 cls: 'control-label',
48577 html: this.fieldLabel
48580 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48586 if(this.indicatorpos == 'right') {
48587 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48594 if(align == 'left') {
48602 if(this.labelWidth > 12){
48603 label.style = "width: " + this.labelWidth + 'px';
48605 if(this.labelWidth < 13 && this.labelmd == 0){
48606 this.labelmd = this.labelWidth;
48608 if(this.labellg > 0){
48609 label.cls += ' col-lg-' + this.labellg;
48610 input.cls += ' col-lg-' + (12 - this.labellg);
48612 if(this.labelmd > 0){
48613 label.cls += ' col-md-' + this.labelmd;
48614 container.cls += ' col-md-' + (12 - this.labelmd);
48616 if(this.labelsm > 0){
48617 label.cls += ' col-sm-' + this.labelsm;
48618 container.cls += ' col-sm-' + (12 - this.labelsm);
48620 if(this.labelxs > 0){
48621 label.cls += ' col-xs-' + this.labelxs;
48622 container.cls += ' col-xs-' + (12 - this.labelxs);
48632 var settings = this;
48634 ['xs','sm','md','lg'].map(function(size){
48635 if (settings[size]) {
48636 cfg.cls += ' col-' + size + '-' + settings[size];
48640 this.store = new Roo.data.Store({
48641 proxy : new Roo.data.MemoryProxy({}),
48642 reader : new Roo.data.JsonReader({
48653 'name' : 'dialCode',
48657 'name' : 'priority',
48661 'name' : 'areaCodes',
48668 if(!this.preferedCountries) {
48669 this.preferedCountries = [
48676 var p = this.preferedCountries.reverse();
48679 for (var i = 0; i < p.length; i++) {
48680 for (var j = 0; j < this.allCountries.length; j++) {
48681 if(this.allCountries[j].iso2 == p[i]) {
48682 var t = this.allCountries[j];
48683 this.allCountries.splice(j,1);
48684 this.allCountries.unshift(t);
48690 this.store.proxy.data = {
48692 data: this.allCountries
48698 initEvents : function()
48701 Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
48703 this.indicator = this.indicatorEl();
48704 this.flag = this.flagEl();
48705 this.dialCodeHolder = this.dialCodeHolderEl();
48707 this.trigger = this.el.select('div.flag-box',true).first();
48708 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
48713 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48714 _this.list.setWidth(lw);
48717 this.list.on('mouseover', this.onViewOver, this);
48718 this.list.on('mousemove', this.onViewMove, this);
48719 this.inputEl().on("keyup", this.onKeyUp, this);
48720 this.inputEl().on("keypress", this.onKeyPress, this);
48722 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
48724 this.view = new Roo.View(this.list, this.tpl, {
48725 singleSelect:true, store: this.store, selectedClass: this.selectedClass
48728 this.view.on('click', this.onViewClick, this);
48729 this.setValue(this.defaultDialCode);
48732 onTriggerClick : function(e)
48734 Roo.log('trigger click');
48739 if(this.isExpanded()){
48741 this.hasFocus = false;
48743 this.store.load({});
48744 this.hasFocus = true;
48749 isExpanded : function()
48751 return this.list.isVisible();
48754 collapse : function()
48756 if(!this.isExpanded()){
48760 Roo.get(document).un('mousedown', this.collapseIf, this);
48761 Roo.get(document).un('mousewheel', this.collapseIf, this);
48762 this.fireEvent('collapse', this);
48766 expand : function()
48770 if(this.isExpanded() || !this.hasFocus){
48774 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
48775 this.list.setWidth(lw);
48778 this.restrictHeight();
48780 Roo.get(document).on('mousedown', this.collapseIf, this);
48781 Roo.get(document).on('mousewheel', this.collapseIf, this);
48783 this.fireEvent('expand', this);
48786 restrictHeight : function()
48788 this.list.alignTo(this.inputEl(), this.listAlign);
48789 this.list.alignTo(this.inputEl(), this.listAlign);
48792 onViewOver : function(e, t)
48794 if(this.inKeyMode){
48797 var item = this.view.findItemFromChild(t);
48800 var index = this.view.indexOf(item);
48801 this.select(index, false);
48806 onViewClick : function(view, doFocus, el, e)
48808 var index = this.view.getSelectedIndexes()[0];
48810 var r = this.store.getAt(index);
48813 this.onSelect(r, index);
48815 if(doFocus !== false && !this.blockFocus){
48816 this.inputEl().focus();
48820 onViewMove : function(e, t)
48822 this.inKeyMode = false;
48825 select : function(index, scrollIntoView)
48827 this.selectedIndex = index;
48828 this.view.select(index);
48829 if(scrollIntoView !== false){
48830 var el = this.view.getNode(index);
48832 this.list.scrollChildIntoView(el, false);
48837 createList : function()
48839 this.list = Roo.get(document.body).createChild({
48841 cls: 'typeahead typeahead-long dropdown-menu tel-list',
48842 style: 'display:none'
48845 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
48848 collapseIf : function(e)
48850 var in_combo = e.within(this.el);
48851 var in_list = e.within(this.list);
48852 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
48854 if (in_combo || in_list || is_list) {
48860 onSelect : function(record, index)
48862 if(this.fireEvent('beforeselect', this, record, index) !== false){
48864 this.setFlagClass(record.data.iso2);
48865 this.setDialCode(record.data.dialCode);
48866 this.hasFocus = false;
48868 this.fireEvent('select', this, record, index);
48872 flagEl : function()
48874 var flag = this.el.select('div.flag',true).first();
48881 dialCodeHolderEl : function()
48883 var d = this.el.select('input.dial-code-holder',true).first();
48890 setDialCode : function(v)
48892 this.dialCodeHolder.dom.value = '+'+v;
48895 setFlagClass : function(n)
48897 this.flag.dom.className = 'flag '+n;
48900 getValue : function()
48902 var v = this.inputEl().getValue();
48903 if(this.dialCodeHolder) {
48904 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
48909 setValue : function(v)
48911 var d = this.getDialCode(v);
48913 //invalid dial code
48914 if(v.length == 0 || !d || d.length == 0) {
48916 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
48917 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48923 this.setFlagClass(this.dialCodeMapping[d].iso2);
48924 this.setDialCode(d);
48925 this.inputEl().dom.value = v.replace('+'+d,'');
48926 this.hiddenEl().dom.value = this.getValue();
48931 getDialCode : function(v)
48935 if (v.length == 0) {
48936 return this.dialCodeHolder.dom.value;
48940 if (v.charAt(0) != "+") {
48943 var numericChars = "";
48944 for (var i = 1; i < v.length; i++) {
48945 var c = v.charAt(i);
48948 if (this.dialCodeMapping[numericChars]) {
48949 dialCode = v.substr(1, i);
48951 if (numericChars.length == 4) {
48961 this.setValue(this.defaultDialCode);
48965 hiddenEl : function()
48967 return this.el.select('input.hidden-tel-input',true).first();
48970 // after setting val
48971 onKeyUp : function(e){
48972 this.setValue(this.getValue());
48975 onKeyPress : function(e){
48976 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48983 * @class Roo.bootstrap.form.MoneyField
48984 * @extends Roo.bootstrap.form.ComboBox
48985 * Bootstrap MoneyField class
48988 * Create a new MoneyField.
48989 * @param {Object} config Configuration options
48992 Roo.bootstrap.form.MoneyField = function(config) {
48994 Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48998 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
49001 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
49003 allowDecimals : true,
49005 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
49007 decimalSeparator : ".",
49009 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
49011 decimalPrecision : 0,
49013 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
49015 allowNegative : true,
49017 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
49021 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
49023 minValue : Number.NEGATIVE_INFINITY,
49025 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
49027 maxValue : Number.MAX_VALUE,
49029 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
49031 minText : "The minimum value for this field is {0}",
49033 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
49035 maxText : "The maximum value for this field is {0}",
49037 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
49038 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
49040 nanText : "{0} is not a valid number",
49042 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
49046 * @cfg {String} defaults currency of the MoneyField
49047 * value should be in lkey
49049 defaultCurrency : false,
49051 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
49053 thousandsDelimiter : false,
49055 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
49064 * @cfg {Roo.data.Store} store Store to lookup currency??
49068 getAutoCreate : function()
49070 var align = this.labelAlign || this.parentLabelAlign();
49082 cls : 'form-control roo-money-amount-input',
49083 autocomplete: 'new-password'
49086 var hiddenInput = {
49090 cls: 'hidden-number-input'
49093 if(this.max_length) {
49094 input.maxlength = this.max_length;
49098 hiddenInput.name = this.name;
49101 if (this.disabled) {
49102 input.disabled = true;
49105 var clg = 12 - this.inputlg;
49106 var cmd = 12 - this.inputmd;
49107 var csm = 12 - this.inputsm;
49108 var cxs = 12 - this.inputxs;
49112 cls : 'row roo-money-field',
49116 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
49120 cls: 'roo-select2-container input-group',
49124 cls : 'form-control roo-money-currency-input',
49125 autocomplete: 'new-password',
49127 name : this.currencyName
49131 cls : 'input-group-addon',
49145 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
49149 cls: this.hasFeedback ? 'has-feedback' : '',
49160 if (this.fieldLabel.length) {
49163 tooltip: 'This field is required'
49169 cls: 'control-label',
49175 html: this.fieldLabel
49178 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
49184 if(this.indicatorpos == 'right') {
49185 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
49192 if(align == 'left') {
49200 if(this.labelWidth > 12){
49201 label.style = "width: " + this.labelWidth + 'px';
49203 if(this.labelWidth < 13 && this.labelmd == 0){
49204 this.labelmd = this.labelWidth;
49206 if(this.labellg > 0){
49207 label.cls += ' col-lg-' + this.labellg;
49208 input.cls += ' col-lg-' + (12 - this.labellg);
49210 if(this.labelmd > 0){
49211 label.cls += ' col-md-' + this.labelmd;
49212 container.cls += ' col-md-' + (12 - this.labelmd);
49214 if(this.labelsm > 0){
49215 label.cls += ' col-sm-' + this.labelsm;
49216 container.cls += ' col-sm-' + (12 - this.labelsm);
49218 if(this.labelxs > 0){
49219 label.cls += ' col-xs-' + this.labelxs;
49220 container.cls += ' col-xs-' + (12 - this.labelxs);
49231 var settings = this;
49233 ['xs','sm','md','lg'].map(function(size){
49234 if (settings[size]) {
49235 cfg.cls += ' col-' + size + '-' + settings[size];
49242 initEvents : function()
49244 this.indicator = this.indicatorEl();
49246 this.initCurrencyEvent();
49248 this.initNumberEvent();
49251 initCurrencyEvent : function()
49254 throw "can not find store for combo";
49257 this.store = Roo.factory(this.store, Roo.data);
49258 this.store.parent = this;
49262 this.triggerEl = this.el.select('.input-group-addon', true).first();
49264 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
49269 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
49270 _this.list.setWidth(lw);
49273 this.list.on('mouseover', this.onViewOver, this);
49274 this.list.on('mousemove', this.onViewMove, this);
49275 this.list.on('scroll', this.onViewScroll, this);
49278 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
49281 this.view = new Roo.View(this.list, this.tpl, {
49282 singleSelect:true, store: this.store, selectedClass: this.selectedClass
49285 this.view.on('click', this.onViewClick, this);
49287 this.store.on('beforeload', this.onBeforeLoad, this);
49288 this.store.on('load', this.onLoad, this);
49289 this.store.on('loadexception', this.onLoadException, this);
49291 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
49292 "up" : function(e){
49293 this.inKeyMode = true;
49297 "down" : function(e){
49298 if(!this.isExpanded()){
49299 this.onTriggerClick();
49301 this.inKeyMode = true;
49306 "enter" : function(e){
49309 if(this.fireEvent("specialkey", this, e)){
49310 this.onViewClick(false);
49316 "esc" : function(e){
49320 "tab" : function(e){
49323 if(this.fireEvent("specialkey", this, e)){
49324 this.onViewClick(false);
49332 doRelay : function(foo, bar, hname){
49333 if(hname == 'down' || this.scope.isExpanded()){
49334 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
49342 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
49346 initNumberEvent : function(e)
49348 this.inputEl().on("keydown" , this.fireKey, this);
49349 this.inputEl().on("focus", this.onFocus, this);
49350 this.inputEl().on("blur", this.onBlur, this);
49352 this.inputEl().relayEvent('keyup', this);
49354 if(this.indicator){
49355 this.indicator.addClass('invisible');
49358 this.originalValue = this.getValue();
49360 if(this.validationEvent == 'keyup'){
49361 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
49362 this.inputEl().on('keyup', this.filterValidation, this);
49364 else if(this.validationEvent !== false){
49365 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
49368 if(this.selectOnFocus){
49369 this.on("focus", this.preFocus, this);
49372 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
49373 this.inputEl().on("keypress", this.filterKeys, this);
49375 this.inputEl().relayEvent('keypress', this);
49378 var allowed = "0123456789";
49380 if(this.allowDecimals){
49381 allowed += this.decimalSeparator;
49384 if(this.allowNegative){
49388 if(this.thousandsDelimiter) {
49392 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
49394 var keyPress = function(e){
49396 var k = e.getKey();
49398 var c = e.getCharCode();
49401 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
49402 allowed.indexOf(String.fromCharCode(c)) === -1
49408 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
49412 if(allowed.indexOf(String.fromCharCode(c)) === -1){
49417 this.inputEl().on("keypress", keyPress, this);
49421 onTriggerClick : function(e)
49428 this.loadNext = false;
49430 if(this.isExpanded()){
49435 this.hasFocus = true;
49437 if(this.triggerAction == 'all') {
49438 this.doQuery(this.allQuery, true);
49442 this.doQuery(this.getRawValue());
49445 getCurrency : function()
49447 var v = this.currencyEl().getValue();
49452 restrictHeight : function()
49454 this.list.alignTo(this.currencyEl(), this.listAlign);
49455 this.list.alignTo(this.currencyEl(), this.listAlign);
49458 onViewClick : function(view, doFocus, el, e)
49460 var index = this.view.getSelectedIndexes()[0];
49462 var r = this.store.getAt(index);
49465 this.onSelect(r, index);
49469 onSelect : function(record, index){
49471 if(this.fireEvent('beforeselect', this, record, index) !== false){
49473 this.setFromCurrencyData(index > -1 ? record.data : false);
49477 this.fireEvent('select', this, record, index);
49481 setFromCurrencyData : function(o)
49485 this.lastCurrency = o;
49487 if (this.currencyField) {
49488 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
49490 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
49493 this.lastSelectionText = currency;
49495 //setting default currency
49496 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
49497 this.setCurrency(this.defaultCurrency);
49501 this.setCurrency(currency);
49504 setFromData : function(o)
49508 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
49510 this.setFromCurrencyData(c);
49515 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
49517 Roo.log('no value set for '+ (this.name ? this.name : this.id));
49520 this.setValue(value);
49524 setCurrency : function(v)
49526 this.currencyValue = v;
49529 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
49534 setValue : function(v)
49536 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
49542 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
49544 this.inputEl().dom.value = (v == '') ? '' :
49545 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
49547 if(!this.allowZero && v === '0') {
49548 this.hiddenEl().dom.value = '';
49549 this.inputEl().dom.value = '';
49556 getRawValue : function()
49558 var v = this.inputEl().getValue();
49563 getValue : function()
49565 return this.fixPrecision(this.parseValue(this.getRawValue()));
49568 parseValue : function(value)
49570 if(this.thousandsDelimiter) {
49572 r = new RegExp(",", "g");
49573 value = value.replace(r, "");
49576 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
49577 return isNaN(value) ? '' : value;
49581 fixPrecision : function(value)
49583 if(this.thousandsDelimiter) {
49585 r = new RegExp(",", "g");
49586 value = value.replace(r, "");
49589 var nan = isNaN(value);
49591 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
49592 return nan ? '' : value;
49594 return parseFloat(value).toFixed(this.decimalPrecision);
49597 decimalPrecisionFcn : function(v)
49599 return Math.floor(v);
49602 validateValue : function(value)
49604 if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
49608 var num = this.parseValue(value);
49611 this.markInvalid(String.format(this.nanText, value));
49615 if(num < this.minValue){
49616 this.markInvalid(String.format(this.minText, this.minValue));
49620 if(num > this.maxValue){
49621 this.markInvalid(String.format(this.maxText, this.maxValue));
49628 validate : function()
49630 if(this.disabled || this.allowBlank){
49635 var currency = this.getCurrency();
49637 if(this.validateValue(this.getRawValue()) && currency.length){
49642 this.markInvalid();
49646 getName: function()
49651 beforeBlur : function()
49657 var v = this.parseValue(this.getRawValue());
49664 onBlur : function()
49668 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
49669 //this.el.removeClass(this.focusClass);
49672 this.hasFocus = false;
49674 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
49678 var v = this.getValue();
49680 if(String(v) !== String(this.startValue)){
49681 this.fireEvent('change', this, v, this.startValue);
49684 this.fireEvent("blur", this);
49687 inputEl : function()
49689 return this.el.select('.roo-money-amount-input', true).first();
49692 currencyEl : function()
49694 return this.el.select('.roo-money-currency-input', true).first();
49697 hiddenEl : function()
49699 return this.el.select('input.hidden-number-input',true).first();
49703 * @class Roo.bootstrap.BezierSignature
49704 * @extends Roo.bootstrap.Component
49705 * Bootstrap BezierSignature class
49706 * This script refer to:
49707 * Title: Signature Pad
49709 * Availability: https://github.com/szimek/signature_pad
49712 * Create a new BezierSignature
49713 * @param {Object} config The config object
49716 Roo.bootstrap.BezierSignature = function(config){
49717 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
49723 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
49730 mouse_btn_down: true,
49733 * @cfg {int} canvas height
49735 canvas_height: '200px',
49738 * @cfg {float|function} Radius of a single dot.
49743 * @cfg {float} Minimum width of a line. Defaults to 0.5.
49748 * @cfg {float} Maximum width of a line. Defaults to 2.5.
49753 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
49758 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
49763 * @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.
49765 bg_color: 'rgba(0, 0, 0, 0)',
49768 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
49770 dot_color: 'black',
49773 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
49775 velocity_filter_weight: 0.7,
49778 * @cfg {function} Callback when stroke begin.
49783 * @cfg {function} Callback when stroke end.
49787 getAutoCreate : function()
49789 var cls = 'roo-signature column';
49792 cls += ' ' + this.cls;
49802 for(var i = 0; i < col_sizes.length; i++) {
49803 if(this[col_sizes[i]]) {
49804 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
49814 cls: 'roo-signature-body',
49818 cls: 'roo-signature-body-canvas',
49819 height: this.canvas_height,
49820 width: this.canvas_width
49827 style: 'display: none'
49835 initEvents: function()
49837 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
49839 var canvas = this.canvasEl();
49841 // mouse && touch event swapping...
49842 canvas.dom.style.touchAction = 'none';
49843 canvas.dom.style.msTouchAction = 'none';
49845 this.mouse_btn_down = false;
49846 canvas.on('mousedown', this._handleMouseDown, this);
49847 canvas.on('mousemove', this._handleMouseMove, this);
49848 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
49850 if (window.PointerEvent) {
49851 canvas.on('pointerdown', this._handleMouseDown, this);
49852 canvas.on('pointermove', this._handleMouseMove, this);
49853 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
49856 if ('ontouchstart' in window) {
49857 canvas.on('touchstart', this._handleTouchStart, this);
49858 canvas.on('touchmove', this._handleTouchMove, this);
49859 canvas.on('touchend', this._handleTouchEnd, this);
49862 Roo.EventManager.onWindowResize(this.resize, this, true);
49864 // file input event
49865 this.fileEl().on('change', this.uploadImage, this);
49872 resize: function(){
49874 var canvas = this.canvasEl().dom;
49875 var ctx = this.canvasElCtx();
49876 var img_data = false;
49878 if(canvas.width > 0) {
49879 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
49881 // setting canvas width will clean img data
49884 var style = window.getComputedStyle ?
49885 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
49887 var padding_left = parseInt(style.paddingLeft) || 0;
49888 var padding_right = parseInt(style.paddingRight) || 0;
49890 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
49893 ctx.putImageData(img_data, 0, 0);
49897 _handleMouseDown: function(e)
49899 if (e.browserEvent.which === 1) {
49900 this.mouse_btn_down = true;
49901 this.strokeBegin(e);
49905 _handleMouseMove: function (e)
49907 if (this.mouse_btn_down) {
49908 this.strokeMoveUpdate(e);
49912 _handleMouseUp: function (e)
49914 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
49915 this.mouse_btn_down = false;
49920 _handleTouchStart: function (e) {
49922 e.preventDefault();
49923 if (e.browserEvent.targetTouches.length === 1) {
49924 // var touch = e.browserEvent.changedTouches[0];
49925 // this.strokeBegin(touch);
49927 this.strokeBegin(e); // assume e catching the correct xy...
49931 _handleTouchMove: function (e) {
49932 e.preventDefault();
49933 // var touch = event.targetTouches[0];
49934 // _this._strokeMoveUpdate(touch);
49935 this.strokeMoveUpdate(e);
49938 _handleTouchEnd: function (e) {
49939 var wasCanvasTouched = e.target === this.canvasEl().dom;
49940 if (wasCanvasTouched) {
49941 e.preventDefault();
49942 // var touch = event.changedTouches[0];
49943 // _this._strokeEnd(touch);
49948 reset: function () {
49949 this._lastPoints = [];
49950 this._lastVelocity = 0;
49951 this._lastWidth = (this.min_width + this.max_width) / 2;
49952 this.canvasElCtx().fillStyle = this.dot_color;
49955 strokeMoveUpdate: function(e)
49957 this.strokeUpdate(e);
49959 if (this.throttle) {
49960 this.throttleStroke(this.strokeUpdate, this.throttle);
49963 this.strokeUpdate(e);
49967 strokeBegin: function(e)
49969 var newPointGroup = {
49970 color: this.dot_color,
49974 if (typeof this.onBegin === 'function') {
49978 this.curve_data.push(newPointGroup);
49980 this.strokeUpdate(e);
49983 strokeUpdate: function(e)
49985 var rect = this.canvasEl().dom.getBoundingClientRect();
49986 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49987 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49988 var lastPoints = lastPointGroup.points;
49989 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49990 var isLastPointTooClose = lastPoint
49991 ? point.distanceTo(lastPoint) <= this.min_distance
49993 var color = lastPointGroup.color;
49994 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49995 var curve = this.addPoint(point);
49997 this.drawDot({color: color, point: point});
50000 this.drawCurve({color: color, curve: curve});
50010 strokeEnd: function(e)
50012 this.strokeUpdate(e);
50013 if (typeof this.onEnd === 'function') {
50018 addPoint: function (point) {
50019 var _lastPoints = this._lastPoints;
50020 _lastPoints.push(point);
50021 if (_lastPoints.length > 2) {
50022 if (_lastPoints.length === 3) {
50023 _lastPoints.unshift(_lastPoints[0]);
50025 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
50026 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
50027 _lastPoints.shift();
50033 calculateCurveWidths: function (startPoint, endPoint) {
50034 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
50035 (1 - this.velocity_filter_weight) * this._lastVelocity;
50037 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
50040 start: this._lastWidth
50043 this._lastVelocity = velocity;
50044 this._lastWidth = newWidth;
50048 drawDot: function (_a) {
50049 var color = _a.color, point = _a.point;
50050 var ctx = this.canvasElCtx();
50051 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
50053 this.drawCurveSegment(point.x, point.y, width);
50055 ctx.fillStyle = color;
50059 drawCurve: function (_a) {
50060 var color = _a.color, curve = _a.curve;
50061 var ctx = this.canvasElCtx();
50062 var widthDelta = curve.endWidth - curve.startWidth;
50063 var drawSteps = Math.floor(curve.length()) * 2;
50065 ctx.fillStyle = color;
50066 for (var i = 0; i < drawSteps; i += 1) {
50067 var t = i / drawSteps;
50073 var x = uuu * curve.startPoint.x;
50074 x += 3 * uu * t * curve.control1.x;
50075 x += 3 * u * tt * curve.control2.x;
50076 x += ttt * curve.endPoint.x;
50077 var y = uuu * curve.startPoint.y;
50078 y += 3 * uu * t * curve.control1.y;
50079 y += 3 * u * tt * curve.control2.y;
50080 y += ttt * curve.endPoint.y;
50081 var width = curve.startWidth + ttt * widthDelta;
50082 this.drawCurveSegment(x, y, width);
50088 drawCurveSegment: function (x, y, width) {
50089 var ctx = this.canvasElCtx();
50091 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
50092 this.is_empty = false;
50097 var ctx = this.canvasElCtx();
50098 var canvas = this.canvasEl().dom;
50099 ctx.fillStyle = this.bg_color;
50100 ctx.clearRect(0, 0, canvas.width, canvas.height);
50101 ctx.fillRect(0, 0, canvas.width, canvas.height);
50102 this.curve_data = [];
50104 this.is_empty = true;
50109 return this.el.select('input',true).first();
50112 canvasEl: function()
50114 return this.el.select('canvas',true).first();
50117 canvasElCtx: function()
50119 return this.el.select('canvas',true).first().dom.getContext('2d');
50122 getImage: function(type)
50124 if(this.is_empty) {
50129 return this.canvasEl().dom.toDataURL('image/'+type, 1);
50132 drawFromImage: function(img_src)
50134 var img = new Image();
50136 img.onload = function(){
50137 this.canvasElCtx().drawImage(img, 0, 0);
50142 this.is_empty = false;
50145 selectImage: function()
50147 this.fileEl().dom.click();
50150 uploadImage: function(e)
50152 var reader = new FileReader();
50154 reader.onload = function(e){
50155 var img = new Image();
50156 img.onload = function(){
50158 this.canvasElCtx().drawImage(img, 0, 0);
50160 img.src = e.target.result;
50163 reader.readAsDataURL(e.target.files[0]);
50166 // Bezier Point Constructor
50167 Point: (function () {
50168 function Point(x, y, time) {
50171 this.time = time || Date.now();
50173 Point.prototype.distanceTo = function (start) {
50174 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
50176 Point.prototype.equals = function (other) {
50177 return this.x === other.x && this.y === other.y && this.time === other.time;
50179 Point.prototype.velocityFrom = function (start) {
50180 return this.time !== start.time
50181 ? this.distanceTo(start) / (this.time - start.time)
50188 // Bezier Constructor
50189 Bezier: (function () {
50190 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
50191 this.startPoint = startPoint;
50192 this.control2 = control2;
50193 this.control1 = control1;
50194 this.endPoint = endPoint;
50195 this.startWidth = startWidth;
50196 this.endWidth = endWidth;
50198 Bezier.fromPoints = function (points, widths, scope) {
50199 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
50200 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
50201 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
50203 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
50204 var dx1 = s1.x - s2.x;
50205 var dy1 = s1.y - s2.y;
50206 var dx2 = s2.x - s3.x;
50207 var dy2 = s2.y - s3.y;
50208 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
50209 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
50210 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
50211 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
50212 var dxm = m1.x - m2.x;
50213 var dym = m1.y - m2.y;
50214 var k = l2 / (l1 + l2);
50215 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
50216 var tx = s2.x - cm.x;
50217 var ty = s2.y - cm.y;
50219 c1: new scope.Point(m1.x + tx, m1.y + ty),
50220 c2: new scope.Point(m2.x + tx, m2.y + ty)
50223 Bezier.prototype.length = function () {
50228 for (var i = 0; i <= steps; i += 1) {
50230 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
50231 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
50233 var xdiff = cx - px;
50234 var ydiff = cy - py;
50235 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
50242 Bezier.prototype.point = function (t, start, c1, c2, end) {
50243 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
50244 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
50245 + (3.0 * c2 * (1.0 - t) * t * t)
50246 + (end * t * t * t);
50251 throttleStroke: function(fn, wait) {
50252 if (wait === void 0) { wait = 250; }
50254 var timeout = null;
50258 var later = function () {
50259 previous = Date.now();
50261 result = fn.apply(storedContext, storedArgs);
50263 storedContext = null;
50267 return function wrapper() {
50269 for (var _i = 0; _i < arguments.length; _i++) {
50270 args[_i] = arguments[_i];
50272 var now = Date.now();
50273 var remaining = wait - (now - previous);
50274 storedContext = this;
50276 if (remaining <= 0 || remaining > wait) {
50278 clearTimeout(timeout);
50282 result = fn.apply(storedContext, storedArgs);
50284 storedContext = null;
50288 else if (!timeout) {
50289 timeout = window.setTimeout(later, remaining);
50299 // old names for form elements
50300 Roo.bootstrap.Form = Roo.bootstrap.form.Form;
50301 Roo.bootstrap.Input = Roo.bootstrap.form.Input;
50302 Roo.bootstrap.TextArea = Roo.bootstrap.form.TextArea;
50303 Roo.bootstrap.TriggerField = Roo.bootstrap.form.TriggerField;
50304 Roo.bootstrap.ComboBox = Roo.bootstrap.form.ComboBox;
50305 Roo.bootstrap.DateField = Roo.bootstrap.form.DateField;
50306 Roo.bootstrap.TimeField = Roo.bootstrap.form.TimeField;
50307 Roo.bootstrap.MonthField = Roo.bootstrap.form.MonthField;
50308 Roo.bootstrap.CheckBox = Roo.bootstrap.form.CheckBox;
50309 Roo.bootstrap.Radio = Roo.bootstrap.form.Radio;
50310 Roo.bootstrap.RadioSet = Roo.bootstrap.form.RadioSet;
50311 Roo.bootstrap.SecurePass = Roo.bootstrap.form.SecurePass;
50312 Roo.bootstrap.FieldLabel = Roo.bootstrap.form.FieldLabel;
50313 Roo.bootstrap.DateSplitField= Roo.bootstrap.form.DateSplitField;
50314 Roo.bootstrap.NumberField = Roo.bootstrap.form.NumberField;
50315 Roo.bootstrap.PhoneInput = Roo.bootstrap.form.PhoneInput;
50316 Roo.bootstrap.PhoneInputData= Roo.bootstrap.form.PhoneInputData;
50317 Roo.bootstrap.MoneyField = Roo.bootstrap.form.MoneyField;
50318 Roo.bootstrap.HtmlEditor = Roo.bootstrap.form.HtmlEditor;
50319 Roo.bootstrap.HtmlEditor.ToolbarStandard = Roo.bootstrap.form.HtmlEditorToolbarStandard;
50320 Roo.bootstrap.Markdown = Roo.bootstrap.form.Markdown;
50321 Roo.bootstrap.CardUploader = Roo.bootstrap.form.CardUploader;// depricated.
50322 Roo.bootstrap.Navbar = Roo.bootstrap.nav.Bar;
50323 Roo.bootstrap.NavGroup = Roo.bootstrap.nav.Group;
50324 Roo.bootstrap.NavHeaderbar = Roo.bootstrap.nav.Headerbar;
50325 Roo.bootstrap.NavItem = Roo.bootstrap.nav.Item;
50327 Roo.bootstrap.NavProgressBar = Roo.bootstrap.nav.ProgressBar;
50328 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
50330 Roo.bootstrap.NavSidebar = Roo.bootstrap.nav.Sidebar;
50331 Roo.bootstrap.NavSidebarItem = Roo.bootstrap.nav.SidebarItem;
50333 Roo.bootstrap.NavSimplebar = Roo.bootstrap.nav.Simplebar;// deprciated
50334 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
50335 Roo.bootstrap.MenuItem = Roo.bootstrap.menu.Item;
50336 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator